SVACE issue resolved
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / visual-base-impl.cpp
1 /*
2  * Copyright (c) 2017 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 "visual-base-impl.h"
20
21 // EXTERNAL HEADER
22 #include <dali/public-api/common/dali-common.h>
23 #include <dali/devel-api/object/handle-devel.h>
24 #include <dali/integration-api/debug.h>
25
26 //INTERNAL HEARDER
27 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
28 #include <dali-toolkit/public-api/visuals/primitive-visual-properties.h>
29 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
30 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
31 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
32
33 namespace
34 {
35 #if defined(DEBUG_ENABLED)
36 Debug::Filter* gVisualBaseLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VISUAL_BASE" );
37 #endif
38 }
39
40 namespace Dali
41 {
42
43 namespace Toolkit
44 {
45
46 namespace Internal
47 {
48
49 Visual::Base::Base( VisualFactoryCache& factoryCache )
50 : mImpl( new Impl() ),
51   mFactoryCache( factoryCache )
52 {
53 }
54
55 Visual::Base::~Base()
56 {
57   delete mImpl;
58 }
59
60 void Visual::Base::SetCustomShader( const Property::Map& shaderMap )
61 {
62   if( mImpl->mCustomShader )
63   {
64     mImpl->mCustomShader->SetPropertyMap( shaderMap );
65   }
66   else
67   {
68     mImpl->mCustomShader = new Impl::CustomShader( shaderMap );
69   }
70 }
71
72 void Visual::Base::SetProperties( const Property::Map& propertyMap )
73 {
74   for( size_t i = 0; i < propertyMap.Count(); ++i )
75   {
76     const KeyValuePair& pair = propertyMap.GetKeyValue( i );
77     const Property::Key& key = pair.first;
78     const Property::Value& value = pair.second;
79
80     Property::Key matchKey = key;
81     if( matchKey.type == Property::Key::STRING )
82     {
83       if( matchKey == CUSTOM_SHADER )
84       {
85         matchKey = Property::Key( DevelVisual::Property::SHADER );
86       }
87       else if( matchKey == TRANSFORM )
88       {
89         matchKey = Property::Key( DevelVisual::Property::TRANSFORM );
90       }
91       else if( matchKey == PREMULTIPLIED_ALPHA )
92       {
93         matchKey = Property::Key( DevelVisual::Property::PREMULTIPLIED_ALPHA );
94       }
95       else if( matchKey == MIX_COLOR )
96       {
97         matchKey = Property::Key( DevelVisual::Property::MIX_COLOR );
98       }
99       else if( matchKey == OPACITY )
100       {
101         matchKey = Property::Key( DevelVisual::Property::OPACITY );
102       }
103     }
104
105     switch( matchKey.indexKey )
106     {
107       case DevelVisual::Property::SHADER:
108       {
109         Property::Map shaderMap;
110         if( value.Get( shaderMap ) )
111         {
112           SetCustomShader( shaderMap );
113         }
114         break;
115       }
116
117       case DevelVisual::Property::TRANSFORM:
118       {
119         Property::Map map;
120         if( value.Get( map ) )
121         {
122           mImpl->mTransform.SetPropertyMap( map );
123         }
124         break;
125       }
126
127       case DevelVisual::Property::PREMULTIPLIED_ALPHA:
128       {
129         bool premultipliedAlpha = false;
130         if( value.Get( premultipliedAlpha ) )
131         {
132           EnablePreMultipliedAlpha( premultipliedAlpha );
133         }
134         break;
135       }
136
137       case DevelVisual::Property::MIX_COLOR:
138       {
139         Vector4 mixColor;
140         if( value.Get( mixColor ) )
141         {
142           if( value.GetType() == Property::VECTOR4 )
143           {
144             SetMixColor( mixColor );
145           }
146           else
147           {
148             Vector3 mixColor3(mixColor);
149             SetMixColor( mixColor3 );
150           }
151         }
152         break;
153       }
154       case DevelVisual::Property::OPACITY:
155       {
156         float opacity;
157         if( value.Get( opacity ) )
158         {
159           mImpl->mMixColor.a = opacity;
160           SetMixColor( mImpl->mMixColor );
161         }
162         break;
163       }
164     }
165   }
166
167   DoSetProperties( propertyMap );
168 }
169
170 void Visual::Base::SetTransformAndSize( const Property::Map& transform, Size controlSize )
171 {
172   mImpl->mControlSize = controlSize;
173   mImpl->mTransform.UpdatePropertyMap( transform );
174
175 #if defined(DEBUG_ENABLED)
176   std::ostringstream oss;
177   oss << transform;
178   DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, "Visual::Base::SetTransformAndSize(%s) - [\e[1;32mtransform: %s  controlSize: (%3.1f, %3.1f)]\e[0m\n",
179                  GetName().c_str(), oss.str().c_str(), controlSize.x, controlSize.y );
180 #endif
181
182   OnSetTransform();
183 }
184
185 void Visual::Base::SetName( const std::string& name )
186 {
187   mImpl->mName = name;
188 }
189
190 const std::string& Visual::Base::GetName()
191 {
192   return mImpl->mName;
193 }
194
195 float Visual::Base::GetHeightForWidth( float width )
196 {
197   float aspectCorrectedHeight = 0.f;
198   Vector2 naturalSize;
199   GetNaturalSize( naturalSize );
200   if( naturalSize.width )
201   {
202     aspectCorrectedHeight = naturalSize.height * width / naturalSize.width;
203   }
204   return aspectCorrectedHeight;
205 }
206
207 float Visual::Base::GetWidthForHeight( float height )
208 {
209   float aspectCorrectedWidth = 0.f;
210   Vector2 naturalSize;
211   GetNaturalSize( naturalSize );
212   if( naturalSize.height > 0.0f )
213   {
214     aspectCorrectedWidth = naturalSize.width * height / naturalSize.height;
215   }
216   return aspectCorrectedWidth;
217 }
218
219 void Visual::Base::GetNaturalSize( Vector2& naturalSize )
220 {
221   naturalSize = Vector2::ZERO;
222 }
223
224 void Visual::Base::SetDepthIndex( float index )
225 {
226   mImpl->mDepthIndex = index;
227   if( mImpl->mRenderer )
228   {
229     mImpl->mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex );
230   }
231 }
232
233 float Visual::Base::GetDepthIndex() const
234 {
235   return mImpl->mDepthIndex;
236 }
237
238 void Visual::Base::SetOnStage( Actor& actor )
239 {
240   if( !IsOnStage() )
241   {
242     // To display the actor correctly, renderer should not be added to actor until all required resources are ready.
243     // Thus the calling of actor.AddRenderer() should happen inside derived class as base class does not know the exact timing.
244     DoSetOnStage( actor );
245
246     if( mImpl->mRenderer )
247     {
248       RegisterMixColor();
249
250       mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
251       mImpl->mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex );
252       mImpl->mFlags |= Impl::IS_ON_STAGE; // Only sets the flag if renderer exists
253     }
254   }
255 }
256
257 void Visual::Base::SetOffStage( Actor& actor )
258 {
259   if( IsOnStage() )
260   {
261     DoSetOffStage( actor );
262     mImpl->mMixColorIndex = Property::INVALID_INDEX;
263     mImpl->mOpacityIndex = Property::INVALID_INDEX;
264     mImpl->mFlags &= ~Impl::IS_ON_STAGE;
265   }
266 }
267
268 void Visual::Base::CreatePropertyMap( Property::Map& map ) const
269 {
270   DoCreatePropertyMap( map );
271
272   if( mImpl->mCustomShader )
273   {
274     mImpl->mCustomShader->CreatePropertyMap( map );
275   }
276
277   Property::Map transform;
278   mImpl->mTransform.GetPropertyMap( transform );
279   map.Insert( DevelVisual::Property::TRANSFORM, transform );
280
281   bool premultipliedAlpha( IsPreMultipliedAlphaEnabled() );
282   map.Insert( DevelVisual::Property::PREMULTIPLIED_ALPHA, premultipliedAlpha );
283
284   // Note, Color and Primitive will also insert their own mix color into the map
285   // which is ok, because they have a different key value range.
286   map.Insert( DevelVisual::Property::MIX_COLOR, mImpl->mMixColor ); // vec4
287   map.Insert( DevelVisual::Property::OPACITY, mImpl->mMixColor.a );
288 }
289
290 void Visual::Base::CreateInstancePropertyMap( Property::Map& map ) const
291 {
292   DoCreateInstancePropertyMap( map );
293
294   if( mImpl->mCustomShader )
295   {
296     mImpl->mCustomShader->CreatePropertyMap( map );
297   }
298
299   //map.Insert( DevelVisual::Property::DEPTH_INDEX, mImpl->mDepthIndex );
300   //map.Insert( DevelVisual::Property::ENABLED, (bool) mImpl->mRenderer );
301 }
302
303
304 void Visual::Base::EnablePreMultipliedAlpha( bool preMultipled )
305 {
306   if( preMultipled )
307   {
308     mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
309   }
310   else
311   {
312     mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
313   }
314
315   if( mImpl->mRenderer )
316   {
317     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultipled);
318   }
319 }
320
321 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
322 {
323   return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
324 }
325
326 void Visual::Base::DoSetOffStage( Actor& actor )
327 {
328   actor.RemoveRenderer( mImpl->mRenderer );
329   mImpl->mRenderer.Reset();
330 }
331
332 bool Visual::Base::IsOnStage() const
333 {
334   return mImpl->mFlags & Impl::IS_ON_STAGE;
335 }
336
337 void Visual::Base::RegisterMixColor()
338 {
339   // Only register if not already registered.
340   // (Color and Primitive visuals will register their own and save to this index)
341   if( mImpl->mMixColorIndex == Property::INVALID_INDEX )
342   {
343     mImpl->mMixColorIndex = DevelHandle::RegisterProperty(
344       mImpl->mRenderer,
345       Toolkit::DevelVisual::Property::MIX_COLOR,
346       MIX_COLOR,
347       Vector3(mImpl->mMixColor) );
348   }
349
350   if( mImpl->mMixColor.a < 1.f )
351   {
352     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
353   }
354
355   if( mImpl->mOpacityIndex == Property::INVALID_INDEX )
356   {
357     mImpl->mOpacityIndex = DevelHandle::RegisterProperty(
358       mImpl->mRenderer,
359       Toolkit::DevelVisual::Property::OPACITY,
360       OPACITY,
361       mImpl->mMixColor.a );
362   }
363
364   float preMultipliedAlpha = 0.0f;
365   if( IsPreMultipliedAlphaEnabled() )
366   {
367     preMultipliedAlpha = 1.0f;
368   }
369   mImpl->mRenderer.RegisterProperty( "preMultipliedAlpha", preMultipliedAlpha );
370 }
371
372 void Visual::Base::SetMixColor( const Vector4& color )
373 {
374   mImpl->mMixColor = color;
375
376   if( mImpl->mRenderer )
377   {
378     mImpl->mRenderer.SetProperty( mImpl->mMixColorIndex, Vector3(color) );
379     mImpl->mRenderer.SetProperty( mImpl->mOpacityIndex, color.a );
380     if( color.a < 1.f )
381     {
382       mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
383     }
384   }
385 }
386
387 void Visual::Base::SetMixColor( const Vector3& color )
388 {
389   mImpl->mMixColor.r = color.r;
390   mImpl->mMixColor.g = color.g;
391   mImpl->mMixColor.b = color.b;
392
393   if( mImpl->mRenderer )
394   {
395     mImpl->mRenderer.SetProperty( mImpl->mMixColorIndex, color );
396   }
397 }
398
399 const Vector4& Visual::Base::GetMixColor() const
400 {
401   return mImpl->mMixColor;
402 }
403
404 Renderer Visual::Base::GetRenderer()
405 {
406   return mImpl->mRenderer;
407 }
408
409 Property::Index Visual::Base::GetPropertyIndex( Property::Key key )
410 {
411   Property::Index index = DevelHandle::GetPropertyIndex( mImpl->mRenderer, key );
412
413   if( index == Property::INVALID_INDEX )
414   {
415     // Is it a shader property?
416     Shader shader = mImpl->mRenderer.GetShader();
417     index = DevelHandle::GetPropertyIndex( shader, key );
418     if( index != Property::INVALID_INDEX )
419     {
420       // Yes - we should register it in the Renderer so it can be set / animated
421       // independently, as shaders are shared across multiple renderers.
422       std::string keyName;
423       Property::Index keyIndex( Property::INVALID_KEY );
424       if( key.type == Property::Key::INDEX )
425       {
426         keyName = shader.GetPropertyName( index );
427         keyIndex = key.indexKey;
428       }
429       else
430       {
431         keyName = key.stringKey;
432         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
433       }
434       Property::Value value = shader.GetProperty( index );
435       index = DevelHandle::RegisterProperty( mImpl->mRenderer, keyIndex, keyName, value );
436     }
437   }
438   return index;
439 }
440
441 void Visual::Base::SetupTransition(
442   Dali::Animation& transition,
443   Internal::TransitionData::Animator& animator,
444   Property::Index index,
445   Property::Value& initialValue,
446   Property::Value& targetValue )
447 {
448   if( index != Property::INVALID_INDEX )
449   {
450     if( mImpl->mRenderer )
451     {
452       if( animator.animate == false )
453       {
454         mImpl->mRenderer.SetProperty( index, targetValue );
455       }
456       else
457       {
458         if( animator.initialValue.GetType() != Property::NONE )
459         {
460           mImpl->mRenderer.SetProperty( index, initialValue );
461         }
462
463         if( ! transition )
464         {
465           transition = Dali::Animation::New( 0.1f );
466         }
467
468         transition.AnimateTo( Property( mImpl->mRenderer, index ),
469                               targetValue,
470                               animator.alphaFunction,
471                               TimePeriod( animator.timePeriodDelay,
472                                           animator.timePeriodDuration ) );
473       }
474     }
475   }
476 }
477
478 void Visual::Base::AnimateProperty(
479   Dali::Animation& transition,
480   Internal::TransitionData::Animator& animator )
481 {
482 #if defined(DEBUG_ENABLED)
483   {
484     std::ostringstream oss;
485     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
486     DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, oss.str().c_str() );
487   }
488 #endif
489
490   Property::Map map;
491   DoCreatePropertyMap( map );
492   Property::Value* valuePtr = map.Find( Toolkit::DevelVisual::Property::TYPE );
493   int visualType = -1;
494   if( valuePtr )
495   {
496     valuePtr->Get( visualType );
497   }
498
499   if( animator.propertyKey == Toolkit::DevelVisual::Property::MIX_COLOR ||
500       animator.propertyKey == MIX_COLOR ||
501       ( visualType == Toolkit::Visual::COLOR &&
502         animator.propertyKey == ColorVisual::Property::MIX_COLOR ) ||
503       ( visualType == Toolkit::Visual::PRIMITIVE &&
504         animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR ) )
505   {
506     AnimateMixColorProperty( transition, animator );
507   }
508   else if(animator.propertyKey == Toolkit::DevelVisual::Property::OPACITY ||
509           animator.propertyKey == OPACITY )
510   {
511     AnimateOpacityProperty( transition, animator );
512   }
513   else if( mImpl->mRenderer )
514   {
515     AnimateRendererProperty( transition, animator );
516   }
517 }
518
519 void Visual::Base::AnimateOpacityProperty(
520   Dali::Animation& transition,
521   Internal::TransitionData::Animator& animator )
522 {
523   Property::Index index = mImpl->mOpacityIndex;
524
525   bool isOpaque = mImpl->mMixColor.a >= 1.0f;
526
527   if( index != Property::INVALID_INDEX )
528   {
529     float initialOpacity;
530     if( animator.initialValue.Get( initialOpacity ) )
531     {
532       isOpaque = (initialOpacity >= 1.0f);
533     }
534
535     float targetOpacity;
536     if( animator.targetValue.Get( targetOpacity ) )
537     {
538       mImpl->mMixColor.a = targetOpacity;
539     }
540
541     SetupTransition( transition, animator, index, animator.initialValue, animator.targetValue );
542     SetupBlendMode( transition, isOpaque, animator.animate );
543   }
544 }
545
546 void Visual::Base::AnimateRendererProperty(
547   Dali::Animation& transition,
548   Internal::TransitionData::Animator& animator )
549 {
550   Property::Index index = GetPropertyIndex( animator.propertyKey );
551   if( index != Property::INVALID_INDEX )
552   {
553     if( animator.targetValue.GetType() != Property::NONE )
554     {
555       // Try writing target value into transform property map
556       // if it's not a valid key, then it won't alter mTransform
557       Property::Map map;
558       if( animator.propertyKey.type == Property::Key::INDEX )
559       {
560         map.Add( animator.propertyKey.indexKey, animator.targetValue );
561       }
562       else
563       {
564         map.Add( animator.propertyKey.stringKey, animator.targetValue );
565       }
566
567       mImpl->mTransform.UpdatePropertyMap( map );
568     }
569
570     SetupTransition( transition, animator, index, animator.initialValue, animator.targetValue );
571   }
572 }
573
574 void Visual::Base::AnimateMixColorProperty(
575   Dali::Animation& transition,
576   Internal::TransitionData::Animator& animator )
577 {
578   Property::Index index = mImpl->mMixColorIndex;
579   bool animateOpacity = false;
580   bool isOpaque = true;
581
582   Property::Value initialOpacity;
583   Property::Value targetOpacity;
584   Property::Value initialMixColor;
585   Property::Value targetMixColor;
586
587   if( index != Property::INVALID_INDEX )
588   {
589     Vector4 initialColor;
590     if( animator.initialValue.Get(initialColor) )
591     {
592       if( animator.initialValue.GetType() == Property::VECTOR4 )
593       {
594         // if there is an initial color specifying alpha, test it
595         isOpaque = initialColor.a >= 1.0f;
596         initialOpacity = initialColor.a;
597       }
598       initialMixColor = Vector3( initialColor );
599     }
600
601     // Set target value into data store
602     if( animator.targetValue.GetType() != Property::NONE )
603     {
604       Vector4 mixColor;
605       animator.targetValue.Get(mixColor);
606       if( animator.targetValue.GetType() == Property::VECTOR4 )
607       {
608         mImpl->mMixColor.a = mixColor.a;
609         targetOpacity = mixColor.a;
610         animateOpacity = true;
611       }
612
613       mImpl->mMixColor.r = mixColor.r;
614       mImpl->mMixColor.g = mixColor.g;
615       mImpl->mMixColor.b = mixColor.b;
616       targetMixColor = Vector3(mixColor);
617     }
618
619     SetupTransition( transition, animator, index, initialMixColor, targetMixColor );
620     if( animateOpacity )
621     {
622       SetupTransition( transition, animator, mImpl->mOpacityIndex, initialOpacity, targetOpacity );
623       SetupBlendMode( transition, isOpaque, animator.animate );
624     }
625   }
626 }
627
628 void Visual::Base::SetupBlendMode( Animation& transition, bool isInitialOpaque, bool animating )
629 {
630   // Ensure the blend mode is turned on if we are animating opacity, and
631   // turned off after the animation ends if the final value is opaque
632   if( ! isInitialOpaque || mImpl->mMixColor.a < 1.0f )
633   {
634     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
635
636     if( animating == true && mImpl->mMixColor.a >= 1.0f )
637     {
638       // When it becomes opaque, set the blend mode back to automatically
639       if( ! mImpl->mBlendSlotDelegate )
640       {
641         mImpl->mBlendSlotDelegate = new SlotDelegate<Visual::Base>(this);
642       }
643       transition.FinishedSignal().Connect( *(mImpl->mBlendSlotDelegate),
644                                            &Visual::Base::OnMixColorFinished );
645     }
646   }
647 }
648
649 void Visual::Base::OnMixColorFinished( Animation& animation )
650 {
651   if( mImpl->mRenderer )
652   {
653     DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, "Visual::Base::OnMixColorFinished()\n");
654     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE,
655                                   ( mImpl->mMixColor.a < 1.0 ) ? BlendMode::ON : BlendMode::AUTO );
656   }
657   delete mImpl->mBlendSlotDelegate;
658   mImpl->mBlendSlotDelegate = NULL;
659 }
660
661 } // namespace Internal
662
663 } // namespace Toolkit
664
665 } // namespace Dali