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>
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->GetInitialDelay() + lhs->GetDuration() ) < ( rhs->GetInitialDelay() + 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 : mDurationSeconds(durationSeconds),
69 mSpeedFactor( speedFactor ),
70 mEndAction(endAction),
71 mDisconnectAction(disconnectAction),
73 mElapsedSeconds(playRange.x*mDurationSeconds),
75 mLoopCount(loopCount),
77 mPlayRange( playRange )
81 Animation::~Animation()
85 void Animation::operator delete( void* ptr )
87 gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
90 void Animation::SetDuration(float durationSeconds)
92 mDurationSeconds = durationSeconds;
95 void Animation::SetLoopCount(int loopCount)
97 mLoopCount = loopCount;
101 void Animation::SetEndAction(Dali::Animation::EndAction action)
106 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
108 if ( mDisconnectAction != action )
110 mDisconnectAction = action;
112 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
114 (*iter)->SetDisconnectAction( action );
119 void Animation::SetPlayRange( const Vector2& range )
123 // Make sure mElapsedSeconds is within the new range
125 if( mState == Stopped )
127 // Ensure that the animation starts at the right place
128 mElapsedSeconds = mPlayRange.x * mDurationSeconds;
132 // If already past the end of the range, but before end of duration, then clamp will
133 // ensure that the animation stops on the next update.
134 // If not yet at the start of the range, clamping will jump to the start
135 mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
139 void Animation::Play()
141 // Sort according to end time with earlier end times coming first, if the end time is the same, then the animators are not moved
142 std::stable_sort( mAnimators.Begin(), mAnimators.End(), CompareAnimatorEndTimes );
146 if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
148 mElapsedSeconds = mPlayRange.y * mDurationSeconds;
151 SetAnimatorsActive( true );
156 void Animation::PlayFrom( float progress )
158 // If the animation is already playing this has no effect
159 // Progress is guaranteed to be in range.
160 if( mState != Playing )
162 mElapsedSeconds = progress * mDurationSeconds;
165 SetAnimatorsActive( true );
169 void Animation::Pause()
171 if (mState == Playing)
177 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
179 if( action == Dali::Animation::BakeFinal )
181 if( mSpeedFactor > 0.0f )
183 mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
187 mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
191 UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
194 void Animation::SetAnimatorsActive( bool active )
196 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
198 (*iter)->SetActive( active );
202 bool Animation::Stop(BufferIndex bufferIndex)
204 bool animationFinished(false);
206 if (mState == Playing || mState == Paused)
208 animationFinished = true; // The actor-thread should be notified of this
210 if( mEndAction != Dali::Animation::Discard )
212 Bake( bufferIndex, mEndAction );
214 // Animators are automatically set to inactive in Bake
218 SetAnimatorsActive( false );
221 // The animation has now been played to completion
226 mElapsedSeconds = mPlayRange.x*mDurationSeconds;
229 return animationFinished;
232 void Animation::OnDestroy(BufferIndex bufferIndex)
234 if (mState == Playing || mState == Paused)
236 if (mEndAction != Dali::Animation::Discard)
238 Bake( bufferIndex, mEndAction );
240 // Animators are automatically set to inactive in Bake
244 SetAnimatorsActive( false );
251 void Animation::AddAnimator( AnimatorBase* animator )
253 animator->ConnectToSceneGraph();
254 animator->SetDisconnectAction( mDisconnectAction );
256 mAnimators.PushBack( animator );
259 void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished )
264 if (mState == Stopped || mState == Destroyed)
266 // Short circuit when animation isn't running
270 // The animation must still be applied when Paused/Stopping
271 if (mState == Playing)
273 mElapsedSeconds += elapsedSeconds * mSpeedFactor;
276 Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
278 if( 0 == mLoopCount )
281 WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
283 UpdateAnimators(bufferIndex, false, false);
285 // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
287 else if( mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
290 looped = (mState == Playing &&
291 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
292 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
294 WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
296 UpdateAnimators(bufferIndex, false, false);
301 // don't increment mPlayedCount until the finished final loop
306 // playing once (and last mCurrentLoop)
307 finished = (mState == Playing &&
308 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
309 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
311 // update with bake if finished
312 UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished);
316 // The animation has now been played to completion
319 // loop iterations come to this else branch for their final iterations
320 if( mCurrentLoop < mLoopCount)
323 DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
326 mElapsedSeconds = playRangeSeconds.x;
332 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
334 const Vector2 playRange( mPlayRange * mDurationSeconds );
335 float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
337 //Loop through all animators
339 for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
341 AnimatorBase *animator = *iter;
343 if( animator->Orphan() )
345 //Remove animators whose PropertyOwner has been destroyed
346 iter = mAnimators.Erase(iter);
350 if( animator->IsEnabled() )
352 const float initialDelay( animator->GetInitialDelay() );
353 if( elapsedSecondsClamped >= initialDelay )
355 // Calculate a progress specific to each individual animator
356 float progress(1.0f);
357 const float animatorDuration = animator->GetDuration();
358 if (animatorDuration > 0.0f) // animators can be "immediate"
360 progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
362 animator->Update(bufferIndex, progress, bake);
371 if ( animationFinished )
373 animator->SetActive( false );
378 INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
387 } // namespace SceneGraph
389 } // namespace Internal