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 #include <dali/integration-api/debug.h>
30 namespace //Unnamed namespace
32 //Memory pool used to allocate new animations. Memory used by this pool will be released when shutting down DALi
33 Dali::Internal::MemoryPoolObjectAllocator<Dali::Internal::SceneGraph::Animation> gAnimationMemoryPool;
35 inline void WrapInPlayRange( float& elapsed, const Dali::Vector2& playRangeSeconds)
37 if( elapsed > playRangeSeconds.y )
39 elapsed = playRangeSeconds.x + fmodf((elapsed-playRangeSeconds.x), (playRangeSeconds.y-playRangeSeconds.x));
41 else if( elapsed < playRangeSeconds.x )
43 elapsed = playRangeSeconds.y - fmodf( (playRangeSeconds.x - elapsed), (playRangeSeconds.y-playRangeSeconds.x) );
47 /// 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.
48 bool CompareAnimatorEndTimes( const Dali::Internal::SceneGraph::AnimatorBase* lhs, const Dali::Internal::SceneGraph::AnimatorBase* rhs )
50 return ( ( lhs->GetIntervalDelay() + lhs->GetDuration() ) < ( rhs->GetIntervalDelay() + rhs->GetDuration() ) );
53 } // unnamed namespace
64 Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction )
66 return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, loopCount, endAction, disconnectAction );
69 Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
70 : mPlayRange( playRange ),
71 mDurationSeconds( durationSeconds ),
72 mDelaySeconds( 0.0f ),
73 mElapsedSeconds( playRange.x*mDurationSeconds ),
74 mSpeedFactor( speedFactor ),
75 mProgressMarker( 0.0f ),
77 mLoopCount(loopCount),
79 mEndAction(endAction),
80 mDisconnectAction(disconnectAction),
82 mProgressReachedSignalRequired( false ),
83 mAutoReverseEnabled( false )
87 Animation::~Animation()
91 void Animation::operator delete( void* ptr )
93 gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
96 void Animation::SetDuration(float durationSeconds)
98 mDurationSeconds = durationSeconds;
101 void Animation::SetProgressNotification( float progress )
103 mProgressMarker = progress;
104 if ( mProgressMarker > 0.0f )
106 mProgressReachedSignalRequired = true;
110 void Animation::SetLoopCount(int loopCount)
112 mLoopCount = loopCount;
116 void Animation::SetEndAction(Dali::Animation::EndAction action)
121 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
123 if ( mDisconnectAction != action )
125 mDisconnectAction = action;
127 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
129 (*iter)->SetDisconnectAction( action );
134 void Animation::SetPlayRange( const Vector2& range )
138 // Make sure mElapsedSeconds is within the new range
140 if( mState == Stopped )
142 // Ensure that the animation starts at the right place
143 mElapsedSeconds = mPlayRange.x * mDurationSeconds;
147 // If already past the end of the range, but before end of duration, then clamp will
148 // ensure that the animation stops on the next update.
149 // If not yet at the start of the range, clamping will jump to the start
150 mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
154 void Animation::Play()
156 DALI_LOG_ERROR("Scene Graph Animation::Play: Before stable_sort\n");
158 // Sort according to end time with earlier end times coming first, if the end time is the same, then the animators are not moved
159 std::stable_sort( mAnimators.Begin(), mAnimators.End(), CompareAnimatorEndTimes );
161 DALI_LOG_ERROR("Scene Graph Animation::Play: After stable_sort\n");
165 if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
167 mElapsedSeconds = mPlayRange.y * mDurationSeconds;
170 DALI_LOG_ERROR("Scene Graph Animation::Play: Before SetAnimatorsActive\n");
172 SetAnimatorsActive( true );
174 DALI_LOG_ERROR("Scene Graph Animation::Play: After SetAnimatorsActive\n");
179 void Animation::PlayFrom( float progress )
181 // If the animation is already playing this has no effect
182 // Progress is guaranteed to be in range.
183 if( mState != Playing )
185 mElapsedSeconds = progress * mDurationSeconds;
188 SetAnimatorsActive( true );
192 void Animation::PlayAfter( float delaySeconds )
194 if( mState != Playing )
196 mDelaySeconds = delaySeconds;
199 if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
201 mElapsedSeconds = mPlayRange.y * mDurationSeconds;
204 SetAnimatorsActive( true );
210 void Animation::Pause()
212 if (mState == Playing)
218 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
220 if( action == Dali::Animation::BakeFinal )
222 if( mSpeedFactor > 0.0f )
224 mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
228 mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
232 UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
235 void Animation::SetAnimatorsActive( bool active )
237 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
239 (*iter)->SetActive( active );
243 bool Animation::Stop(BufferIndex bufferIndex)
245 bool animationFinished(false);
247 if (mState == Playing || mState == Paused)
249 animationFinished = true; // The actor-thread should be notified of this
251 if( mEndAction != Dali::Animation::Discard )
253 Bake( bufferIndex, mEndAction );
255 // Animators are automatically set to inactive in Bake
259 SetAnimatorsActive( false );
262 // The animation has now been played to completion
267 mElapsedSeconds = mPlayRange.x*mDurationSeconds;
270 return animationFinished;
273 void Animation::OnDestroy(BufferIndex bufferIndex)
275 if (mState == Playing || mState == Paused)
277 if (mEndAction != Dali::Animation::Discard)
279 Bake( bufferIndex, mEndAction );
281 // Animators are automatically set to inactive in Bake
285 SetAnimatorsActive( false );
292 void Animation::SetLoopingMode( bool loopingMode )
294 mAutoReverseEnabled = loopingMode;
296 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
298 // Send some variables together to figure out the Animation status
299 (*iter)->SetSpeedFactor( mSpeedFactor );
300 (*iter)->SetLoopCount( mLoopCount );
302 (*iter)->SetLoopingMode( loopingMode );
306 void Animation::AddAnimator( OwnerPointer<AnimatorBase>& animator )
308 animator->ConnectToSceneGraph();
309 animator->SetDisconnectAction( mDisconnectAction );
311 mAnimators.PushBack( animator.Release() );
314 void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
319 if (mState == Stopped || mState == Destroyed)
321 // Short circuit when animation isn't running
325 // The animation must still be applied when Paused/Stopping
326 if (mState == Playing)
328 // If there is delay time before Animation starts, wait the Animation until mDelaySeconds.
329 if( mDelaySeconds > 0.0f )
331 float reduceSeconds = fabsf( elapsedSeconds * mSpeedFactor );
332 if( reduceSeconds > mDelaySeconds )
334 if( mSpeedFactor < 0.0f )
336 mElapsedSeconds -= reduceSeconds - mDelaySeconds;
340 mElapsedSeconds += reduceSeconds - mDelaySeconds;
342 mDelaySeconds = 0.0f;
346 mDelaySeconds -= reduceSeconds;
351 mElapsedSeconds += ( elapsedSeconds * mSpeedFactor );
354 if ( mProgressReachedSignalRequired && ( mElapsedSeconds >= mProgressMarker ) )
356 // The application should be notified by NotificationManager, in another thread
357 progressReached = true;
358 mProgressReachedSignalRequired = false;
362 Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
364 if( 0 == mLoopCount || mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
367 looped = (mState == Playing &&
368 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
369 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
371 WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
373 UpdateAnimators(bufferIndex, false, false );
377 if( mLoopCount != 0 )
381 mProgressReachedSignalRequired = mProgressMarker > 0.0f;
382 // don't increment mPlayedCount until the finished final loop
387 // playing once (and last mCurrentLoop)
388 finished = (mState == Playing &&
389 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
390 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
392 // update with bake if finished
393 UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished );
397 // The animation has now been played to completion
400 // loop iterations come to this else branch for their final iterations
401 if( mCurrentLoop < mLoopCount)
404 DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
407 mProgressReachedSignalRequired = mProgressMarker > 0.0f;
408 mElapsedSeconds = playRangeSeconds.x;
414 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
416 const Vector2 playRange( mPlayRange * mDurationSeconds );
417 float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
419 //Loop through all animators
421 for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
423 AnimatorBase *animator = *iter;
425 if( animator->Orphan() )
427 //Remove animators whose PropertyOwner has been destroyed
428 iter = mAnimators.Erase(iter);
432 if( animator->IsEnabled() )
434 const float intervalDelay( animator->GetIntervalDelay() );
436 if( elapsedSecondsClamped >= intervalDelay )
438 // Calculate a progress specific to each individual animator
439 float progress(1.0f);
440 const float animatorDuration = animator->GetDuration();
441 if (animatorDuration > 0.0f) // animators can be "immediate"
443 progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f , 1.0f );
445 animator->Update(bufferIndex, progress, bake);
454 if ( animationFinished )
456 animator->SetActive( false );
461 INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
470 } // namespace SceneGraph
472 } // namespace Internal