Updated visuals to separate alpha channel from mixColor
[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::EnablePreMultipliedAlpha( bool preMultipled )
291 {
292   if( preMultipled )
293   {
294     mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
295   }
296   else
297   {
298     mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
299   }
300
301   if( mImpl->mRenderer )
302   {
303     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultipled);
304   }
305 }
306
307 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
308 {
309   return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
310 }
311
312 void Visual::Base::DoSetOffStage( Actor& actor )
313 {
314   actor.RemoveRenderer( mImpl->mRenderer );
315   mImpl->mRenderer.Reset();
316 }
317
318 bool Visual::Base::IsOnStage() const
319 {
320   return mImpl->mFlags & Impl::IS_ON_STAGE;
321 }
322
323 bool Visual::Base::IsFromCache() const
324 {
325   return mImpl->mFlags & Impl::IS_FROM_CACHE;
326 }
327
328 void Visual::Base::RegisterMixColor()
329 {
330   // Only register if not already registered.
331   // (Color and Primitive visuals will register their own and save to this index)
332   if( mImpl->mMixColorIndex == Property::INVALID_INDEX )
333   {
334     mImpl->mMixColorIndex = DevelHandle::RegisterProperty(
335       mImpl->mRenderer,
336       Toolkit::DevelVisual::Property::MIX_COLOR,
337       MIX_COLOR,
338       Vector3(mImpl->mMixColor) );
339   }
340
341   if( mImpl->mMixColor.a < 1.f )
342   {
343     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
344   }
345
346   if( mImpl->mOpacityIndex == Property::INVALID_INDEX )
347   {
348     mImpl->mOpacityIndex = DevelHandle::RegisterProperty(
349       mImpl->mRenderer,
350       Toolkit::DevelVisual::Property::OPACITY,
351       OPACITY,
352       mImpl->mMixColor.a );
353   }
354
355   float preMultipliedAlpha = 0.0f;
356   if( IsPreMultipliedAlphaEnabled() )
357   {
358     preMultipliedAlpha = 1.0f;
359   }
360   mImpl->mRenderer.RegisterProperty( "preMultipliedAlpha", preMultipliedAlpha );
361 }
362
363 void Visual::Base::SetMixColor( const Vector4& color )
364 {
365   mImpl->mMixColor = color;
366
367   if( mImpl->mRenderer )
368   {
369     mImpl->mRenderer.SetProperty( mImpl->mMixColorIndex, Vector3(color) );
370     mImpl->mRenderer.SetProperty( mImpl->mOpacityIndex, color.a );
371     if( color.a < 1.f )
372     {
373       mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
374     }
375   }
376 }
377
378 void Visual::Base::SetMixColor( const Vector3& color )
379 {
380   mImpl->mMixColor.r = color.r;
381   mImpl->mMixColor.g = color.g;
382   mImpl->mMixColor.b = color.b;
383
384   if( mImpl->mRenderer )
385   {
386     mImpl->mRenderer.SetProperty( mImpl->mMixColorIndex, color );
387   }
388 }
389
390 const Vector4& Visual::Base::GetMixColor() const
391 {
392   return mImpl->mMixColor;
393 }
394
395 Renderer Visual::Base::GetRenderer()
396 {
397   return mImpl->mRenderer;
398 }
399
400 Property::Index Visual::Base::GetPropertyIndex( Property::Key key )
401 {
402   Property::Index index = DevelHandle::GetPropertyIndex( mImpl->mRenderer, key );
403
404   if( index == Property::INVALID_INDEX )
405   {
406     // Is it a shader property?
407     Shader shader = mImpl->mRenderer.GetShader();
408     index = DevelHandle::GetPropertyIndex( shader, key );
409     if( index != Property::INVALID_INDEX )
410     {
411       // Yes - we should register it in the Renderer so it can be set / animated
412       // independently, as shaders are shared across multiple renderers.
413       std::string keyName;
414       Property::Index keyIndex( Property::INVALID_KEY );
415       if( key.type == Property::Key::INDEX )
416       {
417         keyName = shader.GetPropertyName( index );
418         keyIndex = key.indexKey;
419       }
420       else
421       {
422         keyName = key.stringKey;
423         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
424       }
425       Property::Value value = shader.GetProperty( index );
426       index = DevelHandle::RegisterProperty( mImpl->mRenderer, keyIndex, keyName, value );
427     }
428   }
429   return index;
430 }
431
432 void Visual::Base::SetupTransition(
433   Dali::Animation& transition,
434   Internal::TransitionData::Animator& animator,
435   Property::Index index,
436   Property::Value& initialValue,
437   Property::Value& targetValue )
438 {
439   if( index != Property::INVALID_INDEX )
440   {
441     if( mImpl->mRenderer )
442     {
443       if( animator.animate == false )
444       {
445         mImpl->mRenderer.SetProperty( index, targetValue );
446       }
447       else
448       {
449         if( animator.initialValue.GetType() != Property::NONE )
450         {
451           mImpl->mRenderer.SetProperty( index, initialValue );
452         }
453
454         if( ! transition )
455         {
456           transition = Dali::Animation::New( 0.1f );
457         }
458
459         transition.AnimateTo( Property( mImpl->mRenderer, index ),
460                               targetValue,
461                               animator.alphaFunction,
462                               TimePeriod( animator.timePeriodDelay,
463                                           animator.timePeriodDuration ) );
464       }
465     }
466   }
467 }
468
469 void Visual::Base::AnimateProperty(
470   Dali::Animation& transition,
471   Internal::TransitionData::Animator& animator )
472 {
473 #if defined(DEBUG_ENABLED)
474   {
475     std::ostringstream oss;
476     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
477     DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, oss.str().c_str() );
478   }
479 #endif
480
481   Property::Map map;
482   DoCreatePropertyMap( map );
483   Property::Value* valuePtr = map.Find( Toolkit::DevelVisual::Property::TYPE );
484   int visualType;
485   valuePtr->Get(visualType);
486
487   if( animator.propertyKey == Toolkit::DevelVisual::Property::MIX_COLOR ||
488       animator.propertyKey == MIX_COLOR ||
489       ( visualType == Toolkit::Visual::COLOR &&
490         animator.propertyKey == ColorVisual::Property::MIX_COLOR ) ||
491       ( visualType == Toolkit::Visual::PRIMITIVE &&
492         animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR ) )
493   {
494     AnimateMixColorProperty( transition, animator );
495   }
496   else if(animator.propertyKey == Toolkit::DevelVisual::Property::OPACITY ||
497           animator.propertyKey == OPACITY )
498   {
499     AnimateOpacityProperty( transition, animator );
500   }
501   else if( mImpl->mRenderer )
502   {
503     AnimateRendererProperty(transition, animator);
504   }
505 }
506
507 void Visual::Base::AnimateOpacityProperty(
508   Dali::Animation& transition,
509   Internal::TransitionData::Animator& animator )
510 {
511   Property::Index index = mImpl->mOpacityIndex;
512
513   bool isOpaque = mImpl->mMixColor.a >= 1.0f;
514
515   if( index != Property::INVALID_INDEX )
516   {
517     float initialOpacity;
518     if( animator.initialValue.Get( initialOpacity ) )
519     {
520       isOpaque = (initialOpacity >= 1.0f);
521     }
522
523     float targetOpacity;
524     if( animator.targetValue.Get( targetOpacity ) )
525     {
526       mImpl->mMixColor.a = targetOpacity;
527     }
528
529     SetupTransition( transition, animator, index, animator.initialValue, animator.targetValue );
530     SetupBlendMode( transition, isOpaque, animator.animate );
531   }
532 }
533
534 void Visual::Base::AnimateRendererProperty(
535   Dali::Animation& transition,
536   Internal::TransitionData::Animator& animator )
537 {
538   Property::Index index = GetPropertyIndex( animator.propertyKey );
539   if( index != Property::INVALID_INDEX )
540   {
541     if( animator.targetValue.GetType() != Property::NONE )
542     {
543       // Try writing target value into transform property map
544       // if it's not a valid key, then it won't alter mTransform
545       Property::Map map;
546       if( animator.propertyKey.type == Property::Key::INDEX )
547       {
548         map.Add( animator.propertyKey.indexKey, animator.targetValue );
549       }
550       else
551       {
552         map.Add( animator.propertyKey.stringKey, animator.targetValue );
553       }
554
555       mImpl->mTransform.UpdatePropertyMap( map );
556     }
557
558     SetupTransition( transition, animator, index, animator.initialValue, animator.targetValue );
559   }
560 }
561
562 void Visual::Base::AnimateMixColorProperty(
563   Dali::Animation& transition,
564   Internal::TransitionData::Animator& animator )
565 {
566   Property::Index index = mImpl->mMixColorIndex;
567   bool animateOpacity = false;
568   bool isOpaque = true;
569
570   Property::Value initialOpacity;
571   Property::Value targetOpacity;
572   Property::Value initialMixColor;
573   Property::Value targetMixColor;
574
575   if( index != Property::INVALID_INDEX )
576   {
577     Vector4 initialColor;
578     if( animator.initialValue.Get(initialColor) )
579     {
580       if( animator.initialValue.GetType() == Property::VECTOR4 )
581       {
582         // if there is an initial color specifying alpha, test it
583         isOpaque = initialColor.a >= 1.0f;
584         initialOpacity = initialColor.a;
585       }
586       initialMixColor = Vector3( initialColor );
587     }
588
589     // Set target value into data store
590     if( animator.targetValue.GetType() != Property::NONE )
591     {
592       Vector4 mixColor;
593       animator.targetValue.Get(mixColor);
594       if( animator.targetValue.GetType() == Property::VECTOR4 )
595       {
596         mImpl->mMixColor.a = mixColor.a;
597         targetOpacity = mixColor.a;
598         animateOpacity = true;
599       }
600
601       mImpl->mMixColor.r = mixColor.r;
602       mImpl->mMixColor.g = mixColor.g;
603       mImpl->mMixColor.b = mixColor.b;
604       targetMixColor = Vector3(mixColor);
605     }
606
607     SetupTransition( transition, animator, index, initialMixColor, targetMixColor );
608     if( animateOpacity )
609     {
610       SetupTransition( transition, animator, mImpl->mOpacityIndex, initialOpacity, targetOpacity );
611       SetupBlendMode( transition, isOpaque, animator.animate );
612     }
613   }
614 }
615
616 void Visual::Base::SetupBlendMode( Animation& transition, bool isInitialOpaque, bool animating )
617 {
618   // Ensure the blend mode is turned on if we are animating opacity, and
619   // turned off after the animation ends if the final value is opaque
620   if( ! isInitialOpaque || mImpl->mMixColor.a < 1.0f )
621   {
622     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
623
624     if( animating == true && mImpl->mMixColor.a >= 1.0f )
625     {
626       // When it becomes opaque, set the blend mode back to automatically
627       if( ! mImpl->mBlendSlotDelegate )
628       {
629         mImpl->mBlendSlotDelegate = new SlotDelegate<Visual::Base>(this);
630       }
631       transition.FinishedSignal().Connect( *(mImpl->mBlendSlotDelegate),
632                                            &Visual::Base::OnMixColorFinished );
633     }
634   }
635 }
636
637 void Visual::Base::OnMixColorFinished( Animation& animation )
638 {
639   if( mImpl->mRenderer )
640   {
641     DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, "Visual::Base::OnMixColorFinished()\n");
642     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE,
643                                   ( mImpl->mMixColor.a < 1.0 ) ? BlendMode::ON : BlendMode::AUTO );
644   }
645   delete mImpl->mBlendSlotDelegate;
646   mImpl->mBlendSlotDelegate = NULL;
647 }
648
649 } // namespace Internal
650
651 } // namespace Toolkit
652
653 } // namespace Dali