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 + fmod(elapsed, playRangeSeconds.y);
39 else if( elapsed < playRangeSeconds.x )
41 elapsed = playRangeSeconds.y - fmod(elapsed, playRangeSeconds.y);
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
118 mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
121 void Animation::Play()
125 if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
127 mElapsedSeconds = mPlayRange.y * mDurationSeconds;
130 SetAnimatorsActive( true );
135 void Animation::PlayFrom( float progress )
137 //If the animation is already playing this has no effect
138 if( mState != Playing )
140 mElapsedSeconds = progress * mDurationSeconds;
143 SetAnimatorsActive( true );
147 void Animation::Pause()
149 if (mState == Playing)
155 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
157 if( action == Dali::Animation::BakeFinal )
159 if( mSpeedFactor > 0.0f )
161 mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
165 mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
169 UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
172 void Animation::SetAnimatorsActive( bool active )
174 for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
176 (*iter)->SetActive( active );
180 bool Animation::Stop(BufferIndex bufferIndex)
182 bool animationFinished(false);
184 if (mState == Playing || mState == Paused)
186 animationFinished = true; // The actor-thread should be notified of this
188 if( mEndAction != Dali::Animation::Discard )
190 Bake( bufferIndex, mEndAction );
192 // Animators are automatically set to inactive in Bake
196 SetAnimatorsActive( false );
199 // The animation has now been played to completion
204 mElapsedSeconds = mPlayRange.x*mDurationSeconds;
207 return animationFinished;
210 void Animation::OnDestroy(BufferIndex bufferIndex)
212 if (mState == Playing || mState == Paused)
214 if (mEndAction != Dali::Animation::Discard)
216 Bake( bufferIndex, mEndAction );
218 // Animators are automatically set to inactive in Bake
222 SetAnimatorsActive( false );
229 void Animation::AddAnimator( AnimatorBase* animator )
231 animator->ConnectToSceneGraph();
232 animator->SetDisconnectAction( mDisconnectAction );
234 mAnimators.PushBack( animator );
237 void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished )
242 if (mState == Stopped || mState == Destroyed)
244 // Short circuit when animation isn't running
248 // The animation must still be applied when Paused/Stopping
249 if (mState == Playing)
251 mElapsedSeconds += elapsedSeconds * mSpeedFactor;
254 Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
256 if( 0 == mLoopCount )
259 WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
261 UpdateAnimators(bufferIndex, false, false);
263 // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
265 else if( mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
268 looped = (mState == Playing &&
269 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
270 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
272 WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
274 UpdateAnimators(bufferIndex, false, false);
279 // don't increment mPlayedCount until the finished final loop
284 // playing once (and last mCurrentLoop)
285 finished = (mState == Playing &&
286 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
287 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
289 // update with bake if finished
290 UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished);
294 // The animation has now been played to completion
297 // loop iterations come to this else branch for their final iterations
298 if( mCurrentLoop < mLoopCount)
301 DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
304 mElapsedSeconds = playRangeSeconds.x;
311 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
313 float elapsedSecondsClamped = Clamp( mElapsedSeconds, mPlayRange.x * mDurationSeconds,mPlayRange.y * mDurationSeconds );
315 //Loop through all animators
317 for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
319 AnimatorBase *animator = *iter;
321 if( animator->Orphan() )
323 //Remove animators whose PropertyOwner has been destroyed
324 iter = mAnimators.Erase(iter);
328 if( animator->IsEnabled() )
330 const float initialDelay(animator->GetInitialDelay());
331 if (elapsedSecondsClamped >= initialDelay || mSpeedFactor < 0.0f )
333 // Calculate a progress specific to each individual animator
334 float progress(1.0f);
335 const float animatorDuration = animator->GetDuration();
336 if (animatorDuration > 0.0f) // animators can be "immediate"
338 progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
340 animator->Update(bufferIndex, progress, bake);
349 if ( animationFinished )
351 animator->SetActive( false );
356 INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
365 } // namespace SceneGraph
367 } // namespace Internal