2 * Copyright (c) 2014 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) );
56 Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction )
58 return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, loopCount, endAction, disconnectAction );
61 Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
62 : mDurationSeconds(durationSeconds),
63 mSpeedFactor( speedFactor ),
64 mEndAction(endAction),
65 mDisconnectAction(disconnectAction),
67 mElapsedSeconds(playRange.x*mDurationSeconds),
69 mLoopCount(loopCount),
71 mPlayRange( playRange )
75 Animation::~Animation()
79 void Animation::operator delete( void* ptr )
81 gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
84 void Animation::SetDuration(float durationSeconds)
86 mDurationSeconds = durationSeconds;
89 void Animation::SetLoopCount(int loopCount)
91 mLoopCount = loopCount;
95 void Animation::SetEndAction(Dali::Animation::EndAction action)
100 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
102 if ( mDisconnectAction != action )
104 mDisconnectAction = action;
106 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
108 (*iter)->SetDisconnectAction( action );
113 void Animation::SetPlayRange( const Vector2& range )
117 // Make sure mElapsedSeconds is within the new range
119 if( mState == Stopped )
121 // Ensure that the animation starts at the right place
122 mElapsedSeconds = mPlayRange.x * mDurationSeconds;
126 // If already past the end of the range, but before end of duration, then clamp will
127 // ensure that the animation stops on the next update.
128 // If not yet at the start of the range, clamping will jump to the start
129 mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
133 void Animation::Play()
137 if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
139 mElapsedSeconds = mPlayRange.y * mDurationSeconds;
142 SetAnimatorsActive( true );
147 void Animation::PlayFrom( float progress )
149 // If the animation is already playing this has no effect
150 // Progress is guaranteed to be in range.
151 if( mState != Playing )
153 mElapsedSeconds = progress * mDurationSeconds;
156 SetAnimatorsActive( true );
160 void Animation::Pause()
162 if (mState == Playing)
168 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
170 if( action == Dali::Animation::BakeFinal )
172 if( mSpeedFactor > 0.0f )
174 mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
178 mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
182 UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
185 void Animation::SetAnimatorsActive( bool active )
187 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
189 (*iter)->SetActive( active );
193 bool Animation::Stop(BufferIndex bufferIndex)
195 bool animationFinished(false);
197 if (mState == Playing || mState == Paused)
199 animationFinished = true; // The actor-thread should be notified of this
201 if( mEndAction != Dali::Animation::Discard )
203 Bake( bufferIndex, mEndAction );
205 // Animators are automatically set to inactive in Bake
209 SetAnimatorsActive( false );
212 // The animation has now been played to completion
217 mElapsedSeconds = mPlayRange.x*mDurationSeconds;
220 return animationFinished;
223 void Animation::OnDestroy(BufferIndex bufferIndex)
225 if (mState == Playing || mState == Paused)
227 if (mEndAction != Dali::Animation::Discard)
229 Bake( bufferIndex, mEndAction );
231 // Animators are automatically set to inactive in Bake
235 SetAnimatorsActive( false );
242 void Animation::AddAnimator( AnimatorBase* animator )
244 animator->ConnectToSceneGraph();
245 animator->SetDisconnectAction( mDisconnectAction );
247 mAnimators.PushBack( animator );
250 void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished )
255 if (mState == Stopped || mState == Destroyed)
257 // Short circuit when animation isn't running
261 // The animation must still be applied when Paused/Stopping
262 if (mState == Playing)
264 mElapsedSeconds += elapsedSeconds * mSpeedFactor;
267 Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
269 if( 0 == mLoopCount )
272 WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
274 UpdateAnimators(bufferIndex, false, false);
276 // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
278 else if( mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
281 looped = (mState == Playing &&
282 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
283 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
285 WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
287 UpdateAnimators(bufferIndex, false, false);
292 // don't increment mPlayedCount until the finished final loop
297 // playing once (and last mCurrentLoop)
298 finished = (mState == Playing &&
299 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
300 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
302 // update with bake if finished
303 UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished);
307 // The animation has now been played to completion
310 // loop iterations come to this else branch for their final iterations
311 if( mCurrentLoop < mLoopCount)
314 DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
317 mElapsedSeconds = playRangeSeconds.x;
323 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
325 const Vector2 playRange( mPlayRange * mDurationSeconds );
326 float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
328 //Loop through all animators
330 for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
332 AnimatorBase *animator = *iter;
334 if( animator->Orphan() )
336 //Remove animators whose PropertyOwner has been destroyed
337 iter = mAnimators.Erase(iter);
341 if( animator->IsEnabled() )
343 const float initialDelay( animator->GetInitialDelay() );
344 if( elapsedSecondsClamped >= initialDelay )
346 // Calculate a progress specific to each individual animator
347 float progress(1.0f);
348 const float animatorDuration = animator->GetDuration();
349 if (animatorDuration > 0.0f) // animators can be "immediate"
351 progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
353 animator->Update(bufferIndex, progress, bake);
362 if ( animationFinished )
364 animator->SetActive( false );
369 INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
378 } // namespace SceneGraph
380 } // namespace Internal