2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "visual-base-impl.h"
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>
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>
35 #if defined(DEBUG_ENABLED)
36 Debug::Filter* gVisualBaseLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VISUAL_BASE" );
39 const char * const PRE_MULTIPLIED_ALPHA_PROPERTY( "preMultipliedAlpha" );
51 Visual::Base::Base( VisualFactoryCache& factoryCache )
52 : mImpl( new Impl() ),
53 mFactoryCache( factoryCache )
62 void Visual::Base::SetCustomShader( const Property::Map& shaderMap )
64 if( mImpl->mCustomShader )
66 mImpl->mCustomShader->SetPropertyMap( shaderMap );
70 mImpl->mCustomShader = new Impl::CustomShader( shaderMap );
74 void Visual::Base::SetProperties( const Property::Map& propertyMap )
76 for( size_t i = 0; i < propertyMap.Count(); ++i )
78 const KeyValuePair& pair = propertyMap.GetKeyValue( i );
79 const Property::Key& key = pair.first;
80 const Property::Value& value = pair.second;
82 Property::Key matchKey = key;
83 if( matchKey.type == Property::Key::STRING )
85 if( matchKey == CUSTOM_SHADER )
87 matchKey = Property::Key( Toolkit::Visual::Property::SHADER );
89 else if( matchKey == TRANSFORM )
91 matchKey = Property::Key( Toolkit::Visual::Property::TRANSFORM );
93 else if( matchKey == PREMULTIPLIED_ALPHA )
95 matchKey = Property::Key( Toolkit::Visual::Property::PREMULTIPLIED_ALPHA );
97 else if( matchKey == MIX_COLOR )
99 matchKey = Property::Key( Toolkit::Visual::Property::MIX_COLOR );
101 else if( matchKey == OPACITY )
103 matchKey = Property::Key( Toolkit::Visual::Property::OPACITY );
107 switch( matchKey.indexKey )
109 case Toolkit::Visual::Property::SHADER:
111 Property::Map shaderMap;
112 if( value.Get( shaderMap ) )
114 SetCustomShader( shaderMap );
119 case Toolkit::Visual::Property::TRANSFORM:
122 if( value.Get( map ) )
124 mImpl->mTransform.SetPropertyMap( map );
129 case Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
131 bool premultipliedAlpha = false;
132 if( value.Get( premultipliedAlpha ) )
134 EnablePreMultipliedAlpha( premultipliedAlpha );
139 case Toolkit::Visual::Property::MIX_COLOR:
142 if( value.Get( mixColor ) )
144 if( value.GetType() == Property::VECTOR4 )
146 SetMixColor( mixColor );
150 Vector3 mixColor3(mixColor);
151 SetMixColor( mixColor3 );
156 case Toolkit::Visual::Property::OPACITY:
159 if( value.Get( opacity ) )
161 mImpl->mMixColor.a = opacity;
162 SetMixColor( mImpl->mMixColor );
169 DoSetProperties( propertyMap );
172 void Visual::Base::SetTransformAndSize( const Property::Map& transform, Size controlSize )
174 mImpl->mControlSize = controlSize;
175 mImpl->mTransform.UpdatePropertyMap( transform );
177 #if defined(DEBUG_ENABLED)
178 std::ostringstream oss;
180 DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, "Visual::Base::SetTransformAndSize(%s) - [\e[1;32mtransform: %s controlSize: (%3.1f, %3.1f)]\e[0m\n",
181 GetName().c_str(), oss.str().c_str(), controlSize.x, controlSize.y );
187 void Visual::Base::SetName( const std::string& name )
192 const std::string& Visual::Base::GetName()
197 float Visual::Base::GetHeightForWidth( float width )
199 float aspectCorrectedHeight = 0.f;
201 GetNaturalSize( naturalSize );
202 if( naturalSize.width )
204 aspectCorrectedHeight = naturalSize.height * width / naturalSize.width;
206 return aspectCorrectedHeight;
209 float Visual::Base::GetWidthForHeight( float height )
211 float aspectCorrectedWidth = 0.f;
213 GetNaturalSize( naturalSize );
214 if( naturalSize.height > 0.0f )
216 aspectCorrectedWidth = naturalSize.width * height / naturalSize.height;
218 return aspectCorrectedWidth;
221 void Visual::Base::GetNaturalSize( Vector2& naturalSize )
223 naturalSize = Vector2::ZERO;
226 void Visual::Base::DoAction( const Property::Index actionId, const Property::Value attributes )
228 OnDoAction( actionId, attributes );
231 void Visual::Base::SetDepthIndex( int index )
233 mImpl->mDepthIndex = index;
234 if( mImpl->mRenderer )
236 mImpl->mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex );
240 int Visual::Base::GetDepthIndex() const
242 return mImpl->mDepthIndex;
245 void Visual::Base::SetOnStage( Actor& actor )
249 // To display the actor correctly, renderer should not be added to actor until all required resources are ready.
250 // Thus the calling of actor.AddRenderer() should happen inside derived class as base class does not know the exact timing.
251 DoSetOnStage( actor );
253 if( mImpl->mRenderer )
257 mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
258 mImpl->mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex );
259 mImpl->mFlags |= Impl::IS_ON_STAGE; // Only sets the flag if renderer exists
264 void Visual::Base::SetOffStage( Actor& actor )
268 DoSetOffStage( actor );
269 mImpl->mMixColorIndex = Property::INVALID_INDEX;
270 mImpl->mOpacityIndex = Property::INVALID_INDEX;
271 mImpl->mFlags &= ~Impl::IS_ON_STAGE;
275 void Visual::Base::CreatePropertyMap( Property::Map& map ) const
277 DoCreatePropertyMap( map );
279 if( mImpl->mCustomShader )
281 mImpl->mCustomShader->CreatePropertyMap( map );
284 Property::Map transform;
285 mImpl->mTransform.GetPropertyMap( transform );
286 map.Insert( Toolkit::Visual::Property::TRANSFORM, transform );
288 bool premultipliedAlpha( IsPreMultipliedAlphaEnabled() );
289 map.Insert( Toolkit::Visual::Property::PREMULTIPLIED_ALPHA, premultipliedAlpha );
291 // Note, Color and Primitive will also insert their own mix color into the map
292 // which is ok, because they have a different key value range.
293 map.Insert( Toolkit::Visual::Property::MIX_COLOR, mImpl->mMixColor ); // vec4
294 map.Insert( Toolkit::Visual::Property::OPACITY, mImpl->mMixColor.a );
297 void Visual::Base::CreateInstancePropertyMap( Property::Map& map ) const
299 DoCreateInstancePropertyMap( map );
301 if( mImpl->mCustomShader )
303 mImpl->mCustomShader->CreatePropertyMap( map );
306 //map.Insert( Toolkit::Visual::Property::DEPTH_INDEX, mImpl->mDepthIndex );
307 //map.Insert( Toolkit::Visual::Property::ENABLED, (bool) mImpl->mRenderer );
311 void Visual::Base::EnablePreMultipliedAlpha( bool preMultiplied )
315 mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
319 mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
322 if( mImpl->mRenderer )
324 mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultiplied);
325 mImpl->mRenderer.RegisterProperty( PRE_MULTIPLIED_ALPHA_PROPERTY, static_cast<float>( preMultiplied ) );
329 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
331 return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
334 void Visual::Base::DoSetOffStage( Actor& actor )
336 actor.RemoveRenderer( mImpl->mRenderer );
337 mImpl->mRenderer.Reset();
340 bool Visual::Base::IsOnStage() const
342 return mImpl->mFlags & Impl::IS_ON_STAGE;
345 void Visual::Base::OnDoAction( const Property::Index actionId, const Property::Value& attributes )
347 // May be overriden by derived class
350 void Visual::Base::RegisterMixColor()
352 // Only register if not already registered.
353 // (Color and Primitive visuals will register their own and save to this index)
354 if( mImpl->mMixColorIndex == Property::INVALID_INDEX )
356 mImpl->mMixColorIndex = DevelHandle::RegisterProperty(
358 Toolkit::Visual::Property::MIX_COLOR,
360 Vector3(mImpl->mMixColor) );
363 if( mImpl->mMixColor.a < 1.f )
365 mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
368 if( mImpl->mOpacityIndex == Property::INVALID_INDEX )
370 mImpl->mOpacityIndex = DevelHandle::RegisterProperty(
372 Toolkit::Visual::Property::OPACITY,
374 mImpl->mMixColor.a );
377 float preMultipliedAlpha = 0.0f;
378 if( IsPreMultipliedAlphaEnabled() )
380 preMultipliedAlpha = 1.0f;
382 mImpl->mRenderer.RegisterProperty( PRE_MULTIPLIED_ALPHA_PROPERTY, preMultipliedAlpha );
385 void Visual::Base::SetMixColor( const Vector4& color )
387 mImpl->mMixColor = color;
389 if( mImpl->mRenderer )
391 mImpl->mRenderer.SetProperty( mImpl->mMixColorIndex, Vector3(color) );
392 mImpl->mRenderer.SetProperty( mImpl->mOpacityIndex, color.a );
395 mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
400 void Visual::Base::SetMixColor( const Vector3& color )
402 mImpl->mMixColor.r = color.r;
403 mImpl->mMixColor.g = color.g;
404 mImpl->mMixColor.b = color.b;
406 if( mImpl->mRenderer )
408 mImpl->mRenderer.SetProperty( mImpl->mMixColorIndex, color );
412 const Vector4& Visual::Base::GetMixColor() const
414 return mImpl->mMixColor;
417 void Visual::Base::AddResourceObserver( Visual::ResourceObserver& observer)
419 mImpl->mResourceObserver = &observer;
422 void Visual::Base::RemoveResourceObserver( Visual::ResourceObserver& observer )
424 mImpl->mResourceObserver = NULL;
427 void Visual::Base::ResourceReady(Toolkit::Visual::ResourceStatus resourceStatus)
429 if( mImpl->mResourceStatus != resourceStatus )
431 mImpl->mResourceStatus = resourceStatus;
433 if( mImpl->mResourceObserver )
435 // observer is currently a control impl
436 mImpl->mResourceObserver->ResourceReady( *this );
441 bool Visual::Base::IsResourceReady() const
443 return ( mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY );
446 Toolkit::Visual::ResourceStatus Visual::Base::GetResourceStatus() const
448 return mImpl->mResourceStatus;
451 Renderer Visual::Base::GetRenderer()
453 return mImpl->mRenderer;
456 Property::Index Visual::Base::GetPropertyIndex( Property::Key key )
458 Property::Index index = DevelHandle::GetPropertyIndex( mImpl->mRenderer, key );
460 if( index == Property::INVALID_INDEX )
462 // Is it a shader property?
463 Shader shader = mImpl->mRenderer.GetShader();
464 index = DevelHandle::GetPropertyIndex( shader, key );
465 if( index != Property::INVALID_INDEX )
467 // Yes - we should register it in the Renderer so it can be set / animated
468 // independently, as shaders are shared across multiple renderers.
470 Property::Index keyIndex( Property::INVALID_KEY );
471 if( key.type == Property::Key::INDEX )
473 keyName = shader.GetPropertyName( index );
474 keyIndex = key.indexKey;
478 keyName = key.stringKey;
479 // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
481 Property::Value value = shader.GetProperty( index );
482 index = DevelHandle::RegisterProperty( mImpl->mRenderer, keyIndex, keyName, value );
488 void Visual::Base::SetupTransition(
489 Dali::Animation& transition,
490 Internal::TransitionData::Animator& animator,
491 Property::Index index,
492 Property::Value& initialValue,
493 Property::Value& targetValue )
495 if( index != Property::INVALID_INDEX )
497 if( mImpl->mRenderer )
499 if( animator.animate == false )
501 mImpl->mRenderer.SetProperty( index, targetValue );
505 if( animator.initialValue.GetType() != Property::NONE )
507 mImpl->mRenderer.SetProperty( index, initialValue );
512 transition = Dali::Animation::New( 0.1f );
515 transition.AnimateTo( Property( mImpl->mRenderer, index ),
517 animator.alphaFunction,
518 TimePeriod( animator.timePeriodDelay,
519 animator.timePeriodDuration ) );
525 void Visual::Base::AnimateProperty(
526 Dali::Animation& transition,
527 Internal::TransitionData::Animator& animator )
529 #if defined(DEBUG_ENABLED)
531 std::ostringstream oss;
532 oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
533 DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, oss.str().c_str() );
538 DoCreatePropertyMap( map );
539 Property::Value* valuePtr = map.Find( Toolkit::Visual::Property::TYPE );
543 valuePtr->Get( visualType );
546 if( animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
547 animator.propertyKey == MIX_COLOR ||
548 ( visualType == Toolkit::Visual::COLOR &&
549 animator.propertyKey == ColorVisual::Property::MIX_COLOR ) ||
550 ( visualType == Toolkit::Visual::PRIMITIVE &&
551 animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR ) )
553 AnimateMixColorProperty( transition, animator );
555 else if(animator.propertyKey == Toolkit::Visual::Property::OPACITY ||
556 animator.propertyKey == OPACITY )
558 AnimateOpacityProperty( transition, animator );
560 else if( mImpl->mRenderer )
562 AnimateRendererProperty( transition, animator );
566 void Visual::Base::AnimateOpacityProperty(
567 Dali::Animation& transition,
568 Internal::TransitionData::Animator& animator )
570 Property::Index index = mImpl->mOpacityIndex;
572 bool isOpaque = mImpl->mMixColor.a >= 1.0f;
574 if( index != Property::INVALID_INDEX )
576 float initialOpacity;
577 if( animator.initialValue.Get( initialOpacity ) )
579 isOpaque = (initialOpacity >= 1.0f);
583 if( animator.targetValue.Get( targetOpacity ) )
585 mImpl->mMixColor.a = targetOpacity;
588 SetupTransition( transition, animator, index, animator.initialValue, animator.targetValue );
589 SetupBlendMode( transition, isOpaque, animator.animate );
593 void Visual::Base::AnimateRendererProperty(
594 Dali::Animation& transition,
595 Internal::TransitionData::Animator& animator )
597 Property::Index index = GetPropertyIndex( animator.propertyKey );
598 if( index != Property::INVALID_INDEX )
600 if( animator.targetValue.GetType() != Property::NONE )
602 // Try writing target value into transform property map
603 // if it's not a valid key, then it won't alter mTransform
605 if( animator.propertyKey.type == Property::Key::INDEX )
607 map.Add( animator.propertyKey.indexKey, animator.targetValue );
611 map.Add( animator.propertyKey.stringKey, animator.targetValue );
614 mImpl->mTransform.UpdatePropertyMap( map );
617 SetupTransition( transition, animator, index, animator.initialValue, animator.targetValue );
621 void Visual::Base::AnimateMixColorProperty(
622 Dali::Animation& transition,
623 Internal::TransitionData::Animator& animator )
625 Property::Index index = mImpl->mMixColorIndex;
626 bool animateOpacity = false;
627 bool isOpaque = true;
629 Property::Value initialOpacity;
630 Property::Value targetOpacity;
631 Property::Value initialMixColor;
632 Property::Value targetMixColor;
634 if( index != Property::INVALID_INDEX )
636 Vector4 initialColor;
637 if( animator.initialValue.Get(initialColor) )
639 if( animator.initialValue.GetType() == Property::VECTOR4 )
641 // if there is an initial color specifying alpha, test it
642 isOpaque = initialColor.a >= 1.0f;
643 initialOpacity = initialColor.a;
645 initialMixColor = Vector3( initialColor );
648 // Set target value into data store
649 if( animator.targetValue.GetType() != Property::NONE )
652 animator.targetValue.Get(mixColor);
653 if( animator.targetValue.GetType() == Property::VECTOR4 )
655 mImpl->mMixColor.a = mixColor.a;
656 targetOpacity = mixColor.a;
657 animateOpacity = true;
660 mImpl->mMixColor.r = mixColor.r;
661 mImpl->mMixColor.g = mixColor.g;
662 mImpl->mMixColor.b = mixColor.b;
663 targetMixColor = Vector3(mixColor);
666 SetupTransition( transition, animator, index, initialMixColor, targetMixColor );
669 SetupTransition( transition, animator, mImpl->mOpacityIndex, initialOpacity, targetOpacity );
670 SetupBlendMode( transition, isOpaque, animator.animate );
675 void Visual::Base::SetupBlendMode( Animation& transition, bool isInitialOpaque, bool animating )
677 // Ensure the blend mode is turned on if we are animating opacity, and
678 // turned off after the animation ends if the final value is opaque
679 if( ! isInitialOpaque || mImpl->mMixColor.a < 1.0f )
681 mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
683 if( animating == true && mImpl->mMixColor.a >= 1.0f )
685 // When it becomes opaque, set the blend mode back to automatically
686 if( ! mImpl->mBlendSlotDelegate )
688 mImpl->mBlendSlotDelegate = new SlotDelegate<Visual::Base>(this);
690 transition.FinishedSignal().Connect( *(mImpl->mBlendSlotDelegate),
691 &Visual::Base::OnMixColorFinished );
696 void Visual::Base::OnMixColorFinished( Animation& animation )
698 if( mImpl->mRenderer )
700 DALI_LOG_INFO( gVisualBaseLogFilter, Debug::General, "Visual::Base::OnMixColorFinished()\n");
701 mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE,
702 ( mImpl->mMixColor.a < 1.0 ) ? BlendMode::ON : BlendMode::AUTO );
704 delete mImpl->mBlendSlotDelegate;
705 mImpl->mBlendSlotDelegate = NULL;
708 } // namespace Internal
710 } // namespace Toolkit