Updated visual property animator to handle shader properties
[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/devel-api/visuals/visual-properties-devel.h>
28 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
29 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
30
31 namespace
32 {
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gVisualBaseLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VISUAL_BASE" );
35 #endif
36 }
37
38 namespace Dali
39 {
40
41 namespace Toolkit
42 {
43
44 namespace Internal
45 {
46
47 Visual::Base::Base( VisualFactoryCache& factoryCache )
48 : mImpl( new Impl() ),
49   mFactoryCache( factoryCache )
50 {
51 }
52
53 Visual::Base::~Base()
54 {
55   delete mImpl;
56 }
57
58 void Visual::Base::SetCustomShader( const Property::Map& shaderMap )
59 {
60   if( mImpl->mCustomShader )
61   {
62     mImpl->mCustomShader->SetPropertyMap( shaderMap );
63   }
64   else
65   {
66     mImpl->mCustomShader = new Impl::CustomShader( shaderMap );
67   }
68 }
69
70 void Visual::Base::SetProperties( const Property::Map& propertyMap )
71 {
72   for( size_t i = 0; i < propertyMap.Count(); ++i )
73   {
74     const KeyValuePair& pair = propertyMap.GetKeyValue( i );
75     const Property::Key& key = pair.first;
76     const Property::Value& value = pair.second;
77
78     Property::Key matchKey = key;
79     if( matchKey.type == Property::Key::STRING )
80     {
81       if( matchKey == CUSTOM_SHADER )
82       {
83         matchKey = Property::Key( DevelVisual::Property::SHADER );
84       }
85       else if( matchKey == TRANSFORM )
86       {
87         matchKey = Property::Key( DevelVisual::Property::TRANSFORM );
88       }
89       else if( matchKey == PREMULTIPLIED_ALPHA )
90       {
91         matchKey = Property::Key( DevelVisual::Property::PREMULTIPLIED_ALPHA );
92       }
93       else if( matchKey == MIX_COLOR )
94       {
95         matchKey = Property::Key( DevelVisual::Property::MIX_COLOR );
96       }
97     }
98
99     switch( matchKey.indexKey )
100     {
101       case DevelVisual::Property::SHADER:
102       {
103         Property::Map shaderMap;
104         if( value.Get( shaderMap ) )
105         {
106           SetCustomShader( shaderMap );
107         }
108         break;
109       }
110
111       case DevelVisual::Property::TRANSFORM:
112       {
113         Property::Map map;
114         if( value.Get( map ) )
115         {
116           mImpl->mTransform.SetPropertyMap( map );
117         }
118         break;
119       }
120
121       case DevelVisual::Property::PREMULTIPLIED_ALPHA:
122       {
123         bool premultipliedAlpha = false;
124         if( value.Get( premultipliedAlpha ) )
125         {
126           EnablePreMultipliedAlpha( premultipliedAlpha );
127         }
128         break;
129       }
130
131       case DevelVisual::Property::MIX_COLOR:
132       {
133         Vector4 mixColor;
134         if( value.Get( mixColor ) )
135         {
136           SetMixColor( mixColor );
137         }
138         break;
139       }
140     }
141   }
142
143   DoSetProperties( propertyMap );
144 }
145
146 void Visual::Base::SetTransformAndSize( const Property::Map& transform, Size controlSize )
147 {
148   mImpl->mControlSize = controlSize;
149   mImpl->mTransform.SetPropertyMap( transform );
150
151 #if defined(DEBUG_ENABLED)
152   std::ostringstream oss;
153   oss << transform;
154   DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, "Visual::Base::SetTransformAndSize(%s) - [\e[1;32mtransform: %s  controlSize: (%3.1f, %3.1f)]\e[0m\n",
155                  GetName().c_str(), oss.str().c_str(), controlSize.x, controlSize.y );
156 #endif
157
158   OnSetTransform();
159 }
160
161 void Visual::Base::SetName( const std::string& name )
162 {
163   mImpl->mName = name;
164 }
165
166 const std::string& Visual::Base::GetName()
167 {
168   return mImpl->mName;
169 }
170
171 float Visual::Base::GetHeightForWidth( float width )
172 {
173   float aspectCorrectedHeight = 0.f;
174   Vector2 naturalSize;
175   GetNaturalSize( naturalSize );
176   if( naturalSize.width )
177   {
178     aspectCorrectedHeight = naturalSize.height * width / naturalSize.width;
179   }
180   return aspectCorrectedHeight;
181 }
182
183 float Visual::Base::GetWidthForHeight( float height )
184 {
185   float aspectCorrectedWidth = 0.f;
186   Vector2 naturalSize;
187   GetNaturalSize( naturalSize );
188   if( naturalSize.height > 0.0f )
189   {
190     aspectCorrectedWidth = naturalSize.width * height / naturalSize.height;
191   }
192   return aspectCorrectedWidth;
193 }
194
195 void Visual::Base::GetNaturalSize( Vector2& naturalSize )
196 {
197   naturalSize = Vector2::ZERO;
198 }
199
200 void Visual::Base::SetDepthIndex( float index )
201 {
202   mImpl->mDepthIndex = index;
203   if( mImpl->mRenderer )
204   {
205     mImpl->mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex );
206   }
207 }
208
209 float Visual::Base::GetDepthIndex() const
210 {
211   return mImpl->mDepthIndex;
212 }
213
214 void Visual::Base::SetOnStage( Actor& actor )
215 {
216   if( !IsOnStage() )
217   {
218     // To display the actor correctly, renderer should not be added to actor until all required resources are ready.
219     // Thus the calling of actor.AddRenderer() should happen inside derived class as base class does not know the exact timing.
220     DoSetOnStage( actor );
221
222     if( mImpl->mRenderer )
223     {
224       RegisterMixColor();
225
226       mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
227       mImpl->mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex );
228       mImpl->mFlags |= Impl::IS_ON_STAGE; // Only sets the flag if renderer exists
229     }
230   }
231 }
232
233 void Visual::Base::SetOffStage( Actor& actor )
234 {
235   if( IsOnStage() )
236   {
237     DoSetOffStage( actor );
238     mImpl->mMixColorIndex = Property::INVALID_INDEX;
239     mImpl->mFlags &= ~Impl::IS_ON_STAGE;
240   }
241 }
242
243 void Visual::Base::CreatePropertyMap( Property::Map& map ) const
244 {
245   DoCreatePropertyMap( map );
246
247   if( mImpl->mCustomShader )
248   {
249     mImpl->mCustomShader->CreatePropertyMap( map );
250   }
251
252   Property::Map transform;
253   mImpl->mTransform.GetPropertyMap( transform );
254   map.Insert( DevelVisual::Property::TRANSFORM, transform );
255
256   bool premultipliedAlpha( IsPreMultipliedAlphaEnabled() );
257   map.Insert( DevelVisual::Property::PREMULTIPLIED_ALPHA, premultipliedAlpha );
258
259   // Note, Color and Primitive will also insert their own mix color into the map
260   // which is ok, because they have a different key value range.
261   map.Insert( DevelVisual::Property::MIX_COLOR, GetMixColor() );
262 }
263
264 void Visual::Base::EnablePreMultipliedAlpha( bool preMultipled )
265 {
266   if( preMultipled )
267   {
268     mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
269   }
270   else
271   {
272     mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
273   }
274
275   if( mImpl->mRenderer )
276   {
277     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultipled);
278   }
279 }
280
281 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
282 {
283   return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
284 }
285
286 void Visual::Base::DoSetOffStage( Actor& actor )
287 {
288   actor.RemoveRenderer( mImpl->mRenderer );
289   mImpl->mRenderer.Reset();
290 }
291
292 bool Visual::Base::IsOnStage() const
293 {
294   return mImpl->mFlags & Impl::IS_ON_STAGE;
295 }
296
297 bool Visual::Base::IsFromCache() const
298 {
299   return mImpl->mFlags & Impl::IS_FROM_CACHE;
300 }
301
302 void Visual::Base::RegisterMixColor()
303 {
304   // Only register if not already registered.
305   // (Color and Primitive visuals will register their own and save to this index)
306   if( mImpl->mMixColorIndex == Property::INVALID_INDEX )
307   {
308     mImpl->mMixColorIndex = DevelHandle::RegisterProperty(
309       mImpl->mRenderer,
310       Toolkit::DevelVisual::Property::MIX_COLOR,
311       MIX_COLOR,
312       mImpl->mMixColor );
313
314     if( mImpl->mMixColor.a < 1.f )
315     {
316       mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
317     }
318
319     float preMultipliedAlpha = 0.0f;
320     if( IsPreMultipliedAlphaEnabled() )
321     {
322       preMultipliedAlpha = 1.0f;
323     }
324     mImpl->mRenderer.RegisterProperty( "preMultipliedAlpha", preMultipliedAlpha );
325   }
326 }
327
328 void Visual::Base::SetMixColor( const Vector4& color )
329 {
330   mImpl->mMixColor = color;
331
332   if( mImpl->mRenderer )
333   {
334     mImpl->mRenderer.SetProperty( mImpl->mMixColorIndex, color );
335     if( color.a < 1.f )
336     {
337       mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
338     }
339   }
340 }
341
342 const Vector4& Visual::Base::GetMixColor() const
343 {
344   return mImpl->mMixColor;
345 }
346
347 Renderer Visual::Base::GetRenderer()
348 {
349   return mImpl->mRenderer;
350 }
351
352
353 Property::Index Visual::Base::GetPropertyIndex( Property::Key key )
354 {
355   Property::Index index = DevelHandle::GetPropertyIndex( mImpl->mRenderer, key );
356
357   if( index == Property::INVALID_INDEX )
358   {
359     // Is it a shader property?
360     Shader shader = mImpl->mRenderer.GetShader();
361     index = DevelHandle::GetPropertyIndex( shader, key );
362     if( index != Property::INVALID_INDEX )
363     {
364       // Yes - we should register it in the Renderer so it can be set / animated
365       // independently, as shaders are shared across multiple renderers.
366       std::string keyName;
367       Property::Index keyIndex( Property::INVALID_KEY );
368       if( key.type == Property::Key::INDEX )
369       {
370         keyName = shader.GetPropertyName( index );
371         keyIndex = key.indexKey;
372       }
373       else
374       {
375         keyName = key.stringKey;
376         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
377       }
378       Property::Value value = shader.GetProperty( index );
379       index = DevelHandle::RegisterProperty( mImpl->mRenderer, keyIndex, keyName, value );
380     }
381   }
382   return index;
383 }
384
385 void Visual::Base::SetupTransition(
386   Dali::Animation& transition,
387   Internal::TransitionData::Animator& animator,
388   Property::Index index )
389 {
390   if( index != Property::INVALID_INDEX )
391   {
392     if( mImpl->mRenderer )
393     {
394       if( animator.animate == false )
395       {
396         mImpl->mRenderer.SetProperty( index, animator.targetValue );
397       }
398       else
399       {
400         if( animator.initialValue.GetType() != Property::NONE )
401         {
402           mImpl->mRenderer.SetProperty( index, animator.initialValue );
403         }
404
405         if( ! transition )
406         {
407           transition = Dali::Animation::New( 0.1f );
408         }
409
410         transition.AnimateTo( Property( mImpl->mRenderer, index ),
411                               animator.targetValue,
412                               animator.alphaFunction,
413                               TimePeriod( animator.timePeriodDelay,
414                                           animator.timePeriodDuration ) );
415       }
416     }
417   }
418 }
419
420 void Visual::Base::AnimateProperty(
421   Dali::Animation& transition,
422   Internal::TransitionData::Animator& animator )
423 {
424 #if defined(DEBUG_ENABLED)
425   {
426     std::ostringstream oss;
427     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
428     DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, oss.str().c_str() );
429   }
430 #endif
431
432   Property::Index index = Property::INVALID_INDEX;
433
434   bool isMixColor = false;
435   bool isMixColorOpaque = true;
436
437   // Get the property index
438   if( animator.propertyKey == Toolkit::DevelVisual::Property::MIX_COLOR ||
439       animator.propertyKey == MIX_COLOR )
440   {
441     isMixColor = true;
442     index = mImpl->mMixColorIndex;
443
444     Vector4 initialColor;
445     if( animator.initialValue.Get(initialColor) ) // if there is an initial color, test it
446     {
447       isMixColorOpaque = initialColor.a >= 1.0f;
448     }
449     else
450     {
451       isMixColorOpaque = mImpl->mMixColor.a >= 1.0f; // otherwise, test the current color
452     }
453   }
454   else if( mImpl->mRenderer )
455   {
456     index = GetPropertyIndex( animator.propertyKey );
457   }
458
459   // Set target value into data store
460   if( animator.targetValue.GetType() != Property::NONE )
461   {
462     if( isMixColor )
463     {
464       animator.targetValue.Get( mImpl->mMixColor );
465     }
466     else
467     {
468       // Note: there may be several of these calls if more than one
469       // transform property is animated.
470       Property::Map map;
471       if( animator.propertyKey.type == Property::Key::INDEX )
472       {
473         map.Add( animator.propertyKey.indexKey, animator.targetValue );
474       }
475       else
476       {
477         map.Add( animator.propertyKey.stringKey, animator.targetValue );
478       }
479
480       mImpl->mTransform.UpdatePropertyMap( map );
481     }
482   }
483
484   if( index != Property::INVALID_INDEX )
485   {
486     SetupTransition( transition, animator, index );
487
488     // For mix color, ensure the blend mode is on if the initial or final values are not opaque,
489     // and that it is turned off after the animation ends if the final value is opaque
490     if( isMixColor && (!isMixColorOpaque || mImpl->mMixColor.a < 1.0f) )
491     {
492       mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
493
494       if( animator.animate == true && mImpl->mMixColor.a >= 1.0f )
495       {
496         // When it becomes opaque, set the blend mode back to automatically
497         if( ! mImpl->mBlendSlotDelegate )
498         {
499           mImpl->mBlendSlotDelegate = new SlotDelegate<Visual::Base>(this);
500         }
501         transition.FinishedSignal().Connect( *(mImpl->mBlendSlotDelegate),
502                                              &Visual::Base::OnMixColorFinished );
503       }
504     }
505   }
506 }
507
508 void Visual::Base::OnMixColorFinished( Animation& animation )
509 {
510   if( mImpl->mRenderer )
511   {
512     DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, "Visual::Base::OnMixColorFinished()\n");
513     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE,
514                                   ( mImpl->mMixColor.a < 1.0 ) ? BlendMode::ON : BlendMode::AUTO );
515   }
516   delete mImpl->mBlendSlotDelegate;
517   mImpl->mBlendSlotDelegate = NULL;
518 }
519
520 } // namespace Internal
521
522 } // namespace Toolkit
523
524 } // namespace Dali