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 <dali/internal/update/animation/scene-graph-animation.h>
22 #include <cmath> // fmod
25 #include <dali/internal/common/memory-pool-object-allocator.h>
26 #include <dali/internal/render/common/performance-monitor.h>
27 #include <dali/public-api/math/math-utils.h>
28 namespace //Unnamed namespace
30 //Memory pool used to allocate new animations. Memory used by this pool will be released when shutting down DALi
31 Dali::Internal::MemoryPoolObjectAllocator<Dali::Internal::SceneGraph::Animation> gAnimationMemoryPool;
33 inline void WrapInPlayRange( float& elapsed, const Dali::Vector2& playRangeSeconds)
35 if( elapsed > playRangeSeconds.y )
37 elapsed = playRangeSeconds.x + fmodf((elapsed-playRangeSeconds.x), (playRangeSeconds.y-playRangeSeconds.x));
39 else if( elapsed < playRangeSeconds.x )
41 elapsed = playRangeSeconds.y - fmodf( (playRangeSeconds.x - elapsed), (playRangeSeconds.y-playRangeSeconds.x) );
45 /// Compares the end times of the animators and if the end time is less, then it is moved earlier in the list. If end times are the same, then no change.
46 bool CompareAnimatorEndTimes( const Dali::Internal::SceneGraph::AnimatorBase* lhs, const Dali::Internal::SceneGraph::AnimatorBase* rhs )
48 return ( ( lhs->GetIntervalDelay() + lhs->GetDuration() ) < ( rhs->GetIntervalDelay() + rhs->GetDuration() ) );
51 } // unnamed namespace
62 Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction )
64 return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, loopCount, endAction, disconnectAction );
67 Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
68 : mPlayRange( playRange ),
69 mDurationSeconds( durationSeconds ),
70 mDelaySeconds( 0.0f ),
71 mElapsedSeconds( playRange.x*mDurationSeconds ),
72 mSpeedFactor( speedFactor ),
73 mProgressMarker( 0.0f ),
75 mLoopCount(loopCount),
77 mEndAction(endAction),
78 mDisconnectAction(disconnectAction),
80 mProgressReachedSignalRequired( false ),
81 mAutoReverseEnabled( false )
85 Animation::~Animation()
89 void Animation::operator delete( void* ptr )
91 gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
94 void Animation::SetDuration(float durationSeconds)
96 mDurationSeconds = durationSeconds;
99 void Animation::SetProgressNotification( float progress )
101 mProgressMarker = progress;
102 if ( mProgressMarker > 0.0f )
104 mProgressReachedSignalRequired = true;
108 void Animation::SetLoopCount(int loopCount)
110 mLoopCount = loopCount;
114 void Animation::SetEndAction(Dali::Animation::EndAction action)
119 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
121 if ( mDisconnectAction != action )
123 mDisconnectAction = action;
125 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
127 (*iter)->SetDisconnectAction( action );
132 void Animation::SetPlayRange( const Vector2& range )
136 // Make sure mElapsedSeconds is within the new range
138 if( mState == Stopped )
140 // Ensure that the animation starts at the right place
141 mElapsedSeconds = mPlayRange.x * mDurationSeconds;
145 // If already past the end of the range, but before end of duration, then clamp will
146 // ensure that the animation stops on the next update.
147 // If not yet at the start of the range, clamping will jump to the start
148 mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
152 void Animation::Play()
154 // Sort according to end time with earlier end times coming first, if the end time is the same, then the animators are not moved
155 std::stable_sort( mAnimators.Begin(), mAnimators.End(), CompareAnimatorEndTimes );
159 if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
161 mElapsedSeconds = mPlayRange.y * mDurationSeconds;
164 SetAnimatorsActive( true );
169 void Animation::PlayFrom( float progress )
171 // If the animation is already playing this has no effect
172 // Progress is guaranteed to be in range.
173 if( mState != Playing )
175 mElapsedSeconds = progress * mDurationSeconds;
178 SetAnimatorsActive( true );
182 void Animation::PlayAfter( float delaySeconds )
184 if( mState != Playing )
186 mDelaySeconds = delaySeconds;
189 if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
191 mElapsedSeconds = mPlayRange.y * mDurationSeconds;
194 SetAnimatorsActive( true );
200 void Animation::Pause()
202 if (mState == Playing)
208 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
210 if( action == Dali::Animation::BakeFinal )
212 if( mSpeedFactor > 0.0f )
214 mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
218 mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
222 UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
225 void Animation::SetAnimatorsActive( bool active )
227 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
229 (*iter)->SetActive( active );
233 bool Animation::Stop(BufferIndex bufferIndex)
235 bool animationFinished(false);
237 if (mState == Playing || mState == Paused)
239 animationFinished = true; // The actor-thread should be notified of this
241 if( mEndAction != Dali::Animation::Discard )
243 Bake( bufferIndex, mEndAction );
245 // Animators are automatically set to inactive in Bake
249 SetAnimatorsActive( false );
252 // The animation has now been played to completion
257 mElapsedSeconds = mPlayRange.x*mDurationSeconds;
260 return animationFinished;
263 void Animation::OnDestroy(BufferIndex bufferIndex)
265 if (mState == Playing || mState == Paused)
267 if (mEndAction != Dali::Animation::Discard)
269 Bake( bufferIndex, mEndAction );
271 // Animators are automatically set to inactive in Bake
275 SetAnimatorsActive( false );
282 void Animation::SetLoopingMode( bool loopingMode )
284 mAutoReverseEnabled = loopingMode;
286 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
288 // Send some variables together to figure out the Animation status
289 (*iter)->SetSpeedFactor( mSpeedFactor );
290 (*iter)->SetLoopCount( mLoopCount );
292 (*iter)->SetLoopingMode( loopingMode );
296 void Animation::AddAnimator( OwnerPointer<AnimatorBase>& animator )
298 animator->ConnectToSceneGraph();
299 animator->SetDisconnectAction( mDisconnectAction );
301 mAnimators.PushBack( animator.Release() );
304 void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
309 if (mState == Stopped || mState == Destroyed)
311 // Short circuit when animation isn't running
315 // The animation must still be applied when Paused/Stopping
316 if (mState == Playing)
318 // If there is delay time before Animation starts, wait the Animation until mDelaySeconds.
319 if( mDelaySeconds > 0.0f )
321 float reduceSeconds = fabsf( elapsedSeconds * mSpeedFactor );
322 if( reduceSeconds > mDelaySeconds )
324 if( mSpeedFactor < 0.0f )
326 mElapsedSeconds -= reduceSeconds - mDelaySeconds;
330 mElapsedSeconds += reduceSeconds - mDelaySeconds;
332 mDelaySeconds = 0.0f;
336 mDelaySeconds -= reduceSeconds;
341 mElapsedSeconds += ( elapsedSeconds * mSpeedFactor );
344 if ( mProgressReachedSignalRequired && ( mElapsedSeconds >= mProgressMarker ) )
346 // The application should be notified by NotificationManager, in another thread
347 progressReached = true;
348 mProgressReachedSignalRequired = false;
352 Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
354 if( 0 == mLoopCount )
357 WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
359 UpdateAnimators(bufferIndex, false, false );
361 // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
363 else if( mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
366 looped = (mState == Playing &&
367 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
368 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
370 WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
372 UpdateAnimators(bufferIndex, false, false );
377 mProgressReachedSignalRequired = mProgressMarker > 0.0f;
378 // don't increment mPlayedCount until the finished final loop
383 // playing once (and last mCurrentLoop)
384 finished = (mState == Playing &&
385 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
386 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
388 // update with bake if finished
389 UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished );
393 // The animation has now been played to completion
396 // loop iterations come to this else branch for their final iterations
397 if( mCurrentLoop < mLoopCount)
400 DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
403 mProgressReachedSignalRequired = mProgressMarker > 0.0f;
404 mElapsedSeconds = playRangeSeconds.x;
410 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
412 const Vector2 playRange( mPlayRange * mDurationSeconds );
413 float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
415 //Loop through all animators
417 for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
419 AnimatorBase *animator = *iter;
421 if( animator->Orphan() )
423 //Remove animators whose PropertyOwner has been destroyed
424 iter = mAnimators.Erase(iter);
428 if( animator->IsEnabled() )
430 const float intervalDelay( animator->GetIntervalDelay() );
432 if( elapsedSecondsClamped >= intervalDelay )
434 // Calculate a progress specific to each individual animator
435 float progress(1.0f);
436 const float animatorDuration = animator->GetDuration();
437 if (animatorDuration > 0.0f) // animators can be "immediate"
439 progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f , 1.0f );
441 animator->Update(bufferIndex, progress, bake);
450 if ( animationFinished )
452 animator->SetActive( false );
457 INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
466 } // namespace SceneGraph
468 } // namespace Internal