/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
//Memory pool used to allocate new animations. Memory used by this pool will be released when shutting down DALi
Dali::Internal::MemoryPoolObjectAllocator<Dali::Internal::SceneGraph::Animation> gAnimationMemoryPool;
-inline void WrapInPlayRange( float& elapsed, const Dali::Vector2& playRangeSeconds)
+inline void WrapInPlayRange( float& elapsed, const float& playRangeStartSeconds, const float& playRangeEndSeconds)
{
- if( elapsed > playRangeSeconds.y )
+ if( elapsed > playRangeEndSeconds )
{
- elapsed = playRangeSeconds.x + fmodf((elapsed-playRangeSeconds.x), (playRangeSeconds.y-playRangeSeconds.x));
+ elapsed = playRangeStartSeconds + fmodf( ( elapsed - playRangeStartSeconds ), ( playRangeEndSeconds - playRangeStartSeconds ) );
}
- else if( elapsed < playRangeSeconds.x )
+ else if( elapsed < playRangeStartSeconds )
{
- elapsed = playRangeSeconds.y - fmodf( (playRangeSeconds.x - elapsed), (playRangeSeconds.y-playRangeSeconds.x) );
+ elapsed = playRangeEndSeconds - fmodf( ( playRangeStartSeconds - elapsed ), ( playRangeEndSeconds - playRangeStartSeconds ) );
}
}
namespace SceneGraph
{
-Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction )
+Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int32_t loopCount, EndAction endAction, EndAction disconnectAction )
{
return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, loopCount, endAction, disconnectAction );
}
-Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
+Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int32_t loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
: mPlayRange( playRange ),
mDurationSeconds( durationSeconds ),
mDelaySeconds( 0.0f ),
mDisconnectAction(disconnectAction),
mState(Stopped),
mProgressReachedSignalRequired( false ),
- mAutoReverseEnabled( false )
+ mAutoReverseEnabled( false ),
+ mIsActive{ false }
{
}
-Animation::~Animation()
-{
-}
+Animation::~Animation() = default;
void Animation::operator delete( void* ptr )
{
}
}
-void Animation::SetLoopCount(int loopCount)
+void Animation::SetLoopCount(int32_t loopCount)
{
mLoopCount = loopCount;
mCurrentLoop = 0;
{
mDisconnectAction = action;
- for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
+ for ( auto&& item : mAnimators )
{
- (*iter)->SetDisconnectAction( action );
+ item->SetDisconnectAction( action );
}
}
}
mState = Playing;
SetAnimatorsActive( true );
+
+ mCurrentLoop = 0;
}
}
void Animation::Bake(BufferIndex bufferIndex, EndAction action)
{
- if( action == Dali::Animation::BakeFinal )
+ if( action == Dali::Animation::BAKE_FINAL )
{
if( mSpeedFactor > 0.0f )
{
void Animation::SetAnimatorsActive( bool active )
{
- for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
+ for ( auto&& item : mAnimators )
{
- (*iter)->SetActive( active );
+ item->SetActive( active );
}
}
{
animationFinished = true; // The actor-thread should be notified of this
- if( mEndAction != Dali::Animation::Discard )
+ if( mEndAction != Dali::Animation::DISCARD )
{
Bake( bufferIndex, mEndAction );
{
if (mState == Playing || mState == Paused)
{
- if (mEndAction != Dali::Animation::Discard)
+ if (mEndAction != Dali::Animation::DISCARD)
{
Bake( bufferIndex, mEndAction );
{
mAutoReverseEnabled = loopingMode;
- for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
+ for ( auto&& item : mAnimators )
{
// Send some variables together to figure out the Animation status
- (*iter)->SetSpeedFactor( mSpeedFactor );
- (*iter)->SetLoopCount( mLoopCount );
-
- (*iter)->SetLoopingMode( loopingMode );
+ item->SetSpeedFactor( mSpeedFactor );
+ item->SetLoopCount( mLoopCount );
+ item->SetLoopingMode( loopingMode );
}
}
mAnimators.PushBack( animator.Release() );
}
-void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
+void Animation::Update( BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
{
looped = false;
finished = false;
// The animation must still be applied when Paused/Stopping
if (mState == Playing)
{
+ // Sign value of speed factor. It can optimize many arithmetic comparision
+ float signSpeedFactor = ( mSpeedFactor < 0.0f ) ? -1.f : 1.f;
+
// If there is delay time before Animation starts, wait the Animation until mDelaySeconds.
if( mDelaySeconds > 0.0f )
{
float reduceSeconds = fabsf( elapsedSeconds * mSpeedFactor );
if( reduceSeconds > mDelaySeconds )
{
- if( mSpeedFactor < 0.0f )
- {
- mElapsedSeconds -= reduceSeconds - mDelaySeconds;
- }
- else
- {
- mElapsedSeconds += reduceSeconds - mDelaySeconds;
- }
+ // add overflowed time to mElapsedSecond.
+ // If speed factor > 0, add it. if speed factor < 0, subtract it.
+ float overflowSeconds = reduceSeconds - mDelaySeconds;
+ mElapsedSeconds += signSpeedFactor * overflowSeconds;
mDelaySeconds = 0.0f;
}
else
mElapsedSeconds += ( elapsedSeconds * mSpeedFactor );
}
- if ( mProgressReachedSignalRequired && ( mElapsedSeconds >= mProgressMarker ) )
- {
- // The application should be notified by NotificationManager, in another thread
- progressReached = true;
- mProgressReachedSignalRequired = false;
- }
- }
+ const float playRangeStartSeconds = mPlayRange.x * mDurationSeconds;
+ const float playRangeEndSeconds = mPlayRange.y * mDurationSeconds;
+ // Final reached seconds. It can optimize many arithmetic comparision
+ float edgeRangeSeconds = ( mSpeedFactor < 0.0f ) ? playRangeStartSeconds : playRangeEndSeconds;
- Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
+ // Optimized Factors.
+ // elapsed > edge --> check if looped
+ // elapsed >= marker --> check if elapsed reached to marker in normal case
+ // edge >= marker --> check if elapsed reached to marker in looped case
+ float elapsedFactor = signSpeedFactor * mElapsedSeconds;
+ float edgeFactor = signSpeedFactor * edgeRangeSeconds;
+ float markerFactor = signSpeedFactor * mProgressMarker;
- if( 0 == mLoopCount || mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
- {
- // looping
- looped = (mState == Playing &&
- (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
- ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
+ // check it is looped
+ looped = ( elapsedFactor > edgeFactor );
- WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
+ if( looped )
+ {
+ WrapInPlayRange( mElapsedSeconds, playRangeStartSeconds, playRangeEndSeconds );
- UpdateAnimators(bufferIndex, false, false );
+ // Recalculate elapsedFactor here
+ elapsedFactor = signSpeedFactor * mElapsedSeconds;
- if(looped)
- {
if( mLoopCount != 0 )
{
+ // Check If this animation is finished
++mCurrentLoop;
- }
- mProgressReachedSignalRequired = mProgressMarker > 0.0f;
- // don't increment mPlayedCount until the finished final loop
- }
- }
- else
- {
- // playing once (and last mCurrentLoop)
- finished = (mState == Playing &&
- (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
- ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
+ if( mCurrentLoop >= mLoopCount )
+ {
+ DALI_ASSERT_DEBUG( mCurrentLoop == mLoopCount );
+ finished = true;
- // update with bake if finished
- UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished );
+ // The animation has now been played to completion
+ ++mPlayedCount;
- if(finished)
- {
- // The animation has now been played to completion
- ++mPlayedCount;
+ // Make elapsed second as edge of range forcely.
+ mElapsedSeconds = edgeRangeSeconds + signSpeedFactor * Math::MACHINE_EPSILON_10;
+ UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::DISCARD), finished );
- // loop iterations come to this else branch for their final iterations
- if( mCurrentLoop < mLoopCount)
- {
- ++mCurrentLoop;
- DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
+ // After update animation, mElapsedSeconds must be begin of value
+ mElapsedSeconds = playRangeStartSeconds + playRangeEndSeconds - edgeRangeSeconds;
+ mState = Stopped;
+ }
}
- mProgressReachedSignalRequired = mProgressMarker > 0.0f;
- mElapsedSeconds = playRangeSeconds.x;
- mState = Stopped;
+ // when it is on looped state, 2 case to send progress signal.
+ // (require && range_value >= marker) || << Signal at previous loop
+ // (marker > 0 && !finished && elaped >= marker) << Signal at current loop
+ if( ( mProgressMarker > 0.0f ) && !finished && ( elapsedFactor >= markerFactor ) )
+ {
+ // The application should be notified by NotificationManager, in another thread
+ progressReached = true;
+ mProgressReachedSignalRequired = false;
+ }
+ else
+ {
+ if( mProgressReachedSignalRequired && ( edgeFactor >= markerFactor ) )
+ {
+ progressReached = true;
+ }
+ mProgressReachedSignalRequired = mProgressMarker > 0.0f;
+ }
}
+ else
+ {
+ // when it is not on looped state, only 1 case to send progress signal.
+ // (require && elaped >= marker)
+ if( mProgressReachedSignalRequired && ( elapsedFactor >= markerFactor ) )
+ {
+ // The application should be notified by NotificationManager, in another thread
+ progressReached = true;
+ mProgressReachedSignalRequired = false;
+ }
+ }
+ }
+
+ // Already updated when finished. So skip.
+ if( !finished )
+ {
+ UpdateAnimators(bufferIndex, false, false );
}
}
void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
{
+ mIsActive[bufferIndex] = false;
+
const Vector2 playRange( mPlayRange * mDurationSeconds );
float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
+ bool cleanup = false;
+
//Loop through all animators
- bool applied(true);
- for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
+ for(auto& animator : mAnimators)
{
- AnimatorBase *animator = *iter;
-
- if( animator->Orphan() )
+ if(animator->Orphan())
{
- //Remove animators whose PropertyOwner has been destroyed
- iter = mAnimators.Erase(iter);
+ cleanup = true;
+ continue;
}
- else
+
+ bool applied(true);
+ if(animator->IsEnabled())
{
- if( animator->IsEnabled() )
- {
- const float intervalDelay( animator->GetIntervalDelay() );
+ const float intervalDelay(animator->GetIntervalDelay());
- if( elapsedSecondsClamped >= intervalDelay )
+ if(elapsedSecondsClamped >= intervalDelay)
+ {
+ // Calculate a progress specific to each individual animator
+ float progress(1.0f);
+ const float animatorDuration = animator->GetDuration();
+ if(animatorDuration > 0.0f) // animators can be "immediate"
{
- // Calculate a progress specific to each individual animator
- float progress(1.0f);
- const float animatorDuration = animator->GetDuration();
- if (animatorDuration > 0.0f) // animators can be "immediate"
- {
- progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f , 1.0f );
- }
- animator->Update(bufferIndex, progress, bake);
+ progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f, 1.0f);
}
- applied = true;
- }
- else
- {
- applied = false;
- }
+ animator->Update(bufferIndex, progress, bake);
- if ( animationFinished )
- {
- animator->SetActive( false );
+ if (animatorDuration > 0.0f && (elapsedSecondsClamped - intervalDelay) <= animatorDuration)
+ {
+ mIsActive[bufferIndex] = true;
+ }
}
+ applied = true;
+ }
+ else
+ {
+ applied = false;
+ }
- if (applied)
- {
- INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
- }
+ if(animationFinished)
+ {
+ animator->SetActive(false);
+ }
- ++iter;
+ if(applied)
+ {
+ INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
}
}
+ if(cleanup)
+ {
+ //Remove animators whose PropertyOwner has been destroyed
+ mAnimators.Erase(std::remove_if(mAnimators.begin(),
+ mAnimators.end(),
+ [](auto& animator) { return animator->Orphan(); }),
+ mAnimators.end());
+ }
}
} // namespace SceneGraph