ImageVisual Load policy
[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/public-api/visuals/visual-properties.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( Toolkit::Visual::Property::SHADER );
86       }
87       else if( matchKey == TRANSFORM )
88       {
89         matchKey = Property::Key( Toolkit::Visual::Property::TRANSFORM );
90       }
91       else if( matchKey == PREMULTIPLIED_ALPHA )
92       {
93         matchKey = Property::Key( Toolkit::Visual::Property::PREMULTIPLIED_ALPHA );
94       }
95       else if( matchKey == MIX_COLOR )
96       {
97         matchKey = Property::Key( Toolkit::Visual::Property::MIX_COLOR );
98       }
99       else if( matchKey == OPACITY )
100       {
101         matchKey = Property::Key( Toolkit::Visual::Property::OPACITY );
102       }
103     }
104
105     switch( matchKey.indexKey )
106     {
107       case Toolkit::Visual::Property::SHADER:
108       {
109         Property::Map shaderMap;
110         if( value.Get( shaderMap ) )
111         {
112           SetCustomShader( shaderMap );
113         }
114         break;
115       }
116
117       case Toolkit::Visual::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 Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
128       {
129         bool premultipliedAlpha = false;
130         if( value.Get( premultipliedAlpha ) )
131         {
132           EnablePreMultipliedAlpha( premultipliedAlpha );
133         }
134         break;
135       }
136
137       case Toolkit::Visual::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 Toolkit::Visual::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( int 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 int 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( Toolkit::Visual::Property::TRANSFORM, transform );
280
281   bool premultipliedAlpha( IsPreMultipliedAlphaEnabled() );
282   map.Insert( Toolkit::Visual::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( Toolkit::Visual::Property::MIX_COLOR, mImpl->mMixColor ); // vec4
287   map.Insert( Toolkit::Visual::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( Toolkit::Visual::Property::DEPTH_INDEX, mImpl->mDepthIndex );
300   //map.Insert( Toolkit::Visual::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::Visual::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::Visual::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 void Visual::Base::AddResourceObserver( Visual::ResourceObserver& observer)
405 {
406   mImpl->mResourceObserver = &observer;
407 }
408
409 void Visual::Base::RemoveResourceObserver( Visual::ResourceObserver& observer )
410 {
411   mImpl->mResourceObserver = NULL;
412 }
413
414 void Visual::Base::ResourceReady()
415 {
416   if( mImpl->mResourceReady )
417   {
418     // only inform the observer the first time the resource is ready
419     return;
420   }
421   mImpl->mResourceReady = true;
422
423   if( mImpl->mResourceObserver )
424   {
425     // observer is currently a control impl
426     mImpl->mResourceObserver->ResourceReady( *this );
427   }
428 }
429
430 bool Visual::Base::IsResourceReady() const
431 {
432   return mImpl->mResourceReady;
433 }
434
435 Renderer Visual::Base::GetRenderer()
436 {
437   return mImpl->mRenderer;
438 }
439
440 Property::Index Visual::Base::GetPropertyIndex( Property::Key key )
441 {
442   Property::Index index = DevelHandle::GetPropertyIndex( mImpl->mRenderer, key );
443
444   if( index == Property::INVALID_INDEX )
445   {
446     // Is it a shader property?
447     Shader shader = mImpl->mRenderer.GetShader();
448     index = DevelHandle::GetPropertyIndex( shader, key );
449     if( index != Property::INVALID_INDEX )
450     {
451       // Yes - we should register it in the Renderer so it can be set / animated
452       // independently, as shaders are shared across multiple renderers.
453       std::string keyName;
454       Property::Index keyIndex( Property::INVALID_KEY );
455       if( key.type == Property::Key::INDEX )
456       {
457         keyName = shader.GetPropertyName( index );
458         keyIndex = key.indexKey;
459       }
460       else
461       {
462         keyName = key.stringKey;
463         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
464       }
465       Property::Value value = shader.GetProperty( index );
466       index = DevelHandle::RegisterProperty( mImpl->mRenderer, keyIndex, keyName, value );
467     }
468   }
469   return index;
470 }
471
472 void Visual::Base::SetupTransition(
473   Dali::Animation& transition,
474   Internal::TransitionData::Animator& animator,
475   Property::Index index,
476   Property::Value& initialValue,
477   Property::Value& targetValue )
478 {
479   if( index != Property::INVALID_INDEX )
480   {
481     if( mImpl->mRenderer )
482     {
483       if( animator.animate == false )
484       {
485         mImpl->mRenderer.SetProperty( index, targetValue );
486       }
487       else
488       {
489         if( animator.initialValue.GetType() != Property::NONE )
490         {
491           mImpl->mRenderer.SetProperty( index, initialValue );
492         }
493
494         if( ! transition )
495         {
496           transition = Dali::Animation::New( 0.1f );
497         }
498
499         transition.AnimateTo( Property( mImpl->mRenderer, index ),
500                               targetValue,
501                               animator.alphaFunction,
502                               TimePeriod( animator.timePeriodDelay,
503                                           animator.timePeriodDuration ) );
504       }
505     }
506   }
507 }
508
509 void Visual::Base::AnimateProperty(
510   Dali::Animation& transition,
511   Internal::TransitionData::Animator& animator )
512 {
513 #if defined(DEBUG_ENABLED)
514   {
515     std::ostringstream oss;
516     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
517     DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, oss.str().c_str() );
518   }
519 #endif
520
521   Property::Map map;
522   DoCreatePropertyMap( map );
523   Property::Value* valuePtr = map.Find( Toolkit::Visual::Property::TYPE );
524   int visualType = -1;
525   if( valuePtr )
526   {
527     valuePtr->Get( visualType );
528   }
529
530   if( animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
531       animator.propertyKey == MIX_COLOR ||
532       ( visualType == Toolkit::Visual::COLOR &&
533         animator.propertyKey == ColorVisual::Property::MIX_COLOR ) ||
534       ( visualType == Toolkit::Visual::PRIMITIVE &&
535         animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR ) )
536   {
537     AnimateMixColorProperty( transition, animator );
538   }
539   else if(animator.propertyKey == Toolkit::Visual::Property::OPACITY ||
540           animator.propertyKey == OPACITY )
541   {
542     AnimateOpacityProperty( transition, animator );
543   }
544   else if( mImpl->mRenderer )
545   {
546     AnimateRendererProperty( transition, animator );
547   }
548 }
549
550 void Visual::Base::AnimateOpacityProperty(
551   Dali::Animation& transition,
552   Internal::TransitionData::Animator& animator )
553 {
554   Property::Index index = mImpl->mOpacityIndex;
555
556   bool isOpaque = mImpl->mMixColor.a >= 1.0f;
557
558   if( index != Property::INVALID_INDEX )
559   {
560     float initialOpacity;
561     if( animator.initialValue.Get( initialOpacity ) )
562     {
563       isOpaque = (initialOpacity >= 1.0f);
564     }
565
566     float targetOpacity;
567     if( animator.targetValue.Get( targetOpacity ) )
568     {
569       mImpl->mMixColor.a = targetOpacity;
570     }
571
572     SetupTransition( transition, animator, index, animator.initialValue, animator.targetValue );
573     SetupBlendMode( transition, isOpaque, animator.animate );
574   }
575 }
576
577 void Visual::Base::AnimateRendererProperty(
578   Dali::Animation& transition,
579   Internal::TransitionData::Animator& animator )
580 {
581   Property::Index index = GetPropertyIndex( animator.propertyKey );
582   if( index != Property::INVALID_INDEX )
583   {
584     if( animator.targetValue.GetType() != Property::NONE )
585     {
586       // Try writing target value into transform property map
587       // if it's not a valid key, then it won't alter mTransform
588       Property::Map map;
589       if( animator.propertyKey.type == Property::Key::INDEX )
590       {
591         map.Add( animator.propertyKey.indexKey, animator.targetValue );
592       }
593       else
594       {
595         map.Add( animator.propertyKey.stringKey, animator.targetValue );
596       }
597
598       mImpl->mTransform.UpdatePropertyMap( map );
599     }
600
601     SetupTransition( transition, animator, index, animator.initialValue, animator.targetValue );
602   }
603 }
604
605 void Visual::Base::AnimateMixColorProperty(
606   Dali::Animation& transition,
607   Internal::TransitionData::Animator& animator )
608 {
609   Property::Index index = mImpl->mMixColorIndex;
610   bool animateOpacity = false;
611   bool isOpaque = true;
612
613   Property::Value initialOpacity;
614   Property::Value targetOpacity;
615   Property::Value initialMixColor;
616   Property::Value targetMixColor;
617
618   if( index != Property::INVALID_INDEX )
619   {
620     Vector4 initialColor;
621     if( animator.initialValue.Get(initialColor) )
622     {
623       if( animator.initialValue.GetType() == Property::VECTOR4 )
624       {
625         // if there is an initial color specifying alpha, test it
626         isOpaque = initialColor.a >= 1.0f;
627         initialOpacity = initialColor.a;
628       }
629       initialMixColor = Vector3( initialColor );
630     }
631
632     // Set target value into data store
633     if( animator.targetValue.GetType() != Property::NONE )
634     {
635       Vector4 mixColor;
636       animator.targetValue.Get(mixColor);
637       if( animator.targetValue.GetType() == Property::VECTOR4 )
638       {
639         mImpl->mMixColor.a = mixColor.a;
640         targetOpacity = mixColor.a;
641         animateOpacity = true;
642       }
643
644       mImpl->mMixColor.r = mixColor.r;
645       mImpl->mMixColor.g = mixColor.g;
646       mImpl->mMixColor.b = mixColor.b;
647       targetMixColor = Vector3(mixColor);
648     }
649
650     SetupTransition( transition, animator, index, initialMixColor, targetMixColor );
651     if( animateOpacity )
652     {
653       SetupTransition( transition, animator, mImpl->mOpacityIndex, initialOpacity, targetOpacity );
654       SetupBlendMode( transition, isOpaque, animator.animate );
655     }
656   }
657 }
658
659 void Visual::Base::SetupBlendMode( Animation& transition, bool isInitialOpaque, bool animating )
660 {
661   // Ensure the blend mode is turned on if we are animating opacity, and
662   // turned off after the animation ends if the final value is opaque
663   if( ! isInitialOpaque || mImpl->mMixColor.a < 1.0f )
664   {
665     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
666
667     if( animating == true && mImpl->mMixColor.a >= 1.0f )
668     {
669       // When it becomes opaque, set the blend mode back to automatically
670       if( ! mImpl->mBlendSlotDelegate )
671       {
672         mImpl->mBlendSlotDelegate = new SlotDelegate<Visual::Base>(this);
673       }
674       transition.FinishedSignal().Connect( *(mImpl->mBlendSlotDelegate),
675                                            &Visual::Base::OnMixColorFinished );
676     }
677   }
678 }
679
680 void Visual::Base::OnMixColorFinished( Animation& animation )
681 {
682   if( mImpl->mRenderer )
683   {
684     DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, "Visual::Base::OnMixColorFinished()\n");
685     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE,
686                                   ( mImpl->mMixColor.a < 1.0 ) ? BlendMode::ON : BlendMode::AUTO );
687   }
688   delete mImpl->mBlendSlotDelegate;
689   mImpl->mBlendSlotDelegate = NULL;
690 }
691
692 } // namespace Internal
693
694 } // namespace Toolkit
695
696 } // namespace Dali