2 * Copyright (c) 2020 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 float& playRangeStartSeconds, const float& playRangeEndSeconds)
35 if( elapsed > playRangeEndSeconds )
37 elapsed = playRangeStartSeconds + fmodf( ( elapsed - playRangeStartSeconds ), ( playRangeEndSeconds - playRangeStartSeconds ) );
39 else if( elapsed < playRangeStartSeconds )
41 elapsed = playRangeEndSeconds - fmodf( ( playRangeStartSeconds - elapsed ), ( playRangeEndSeconds - playRangeStartSeconds ) );
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, int32_t 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, int32_t 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 ),
86 Animation::~Animation()
90 void Animation::operator delete( void* ptr )
92 gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
95 void Animation::SetDuration(float durationSeconds)
97 mDurationSeconds = durationSeconds;
100 void Animation::SetProgressNotification( float progress )
102 mProgressMarker = progress;
103 if ( mProgressMarker > 0.0f )
105 mProgressReachedSignalRequired = true;
109 void Animation::SetLoopCount(int32_t loopCount)
111 mLoopCount = loopCount;
115 void Animation::SetEndAction(Dali::Animation::EndAction action)
120 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
122 if ( mDisconnectAction != action )
124 mDisconnectAction = action;
126 for ( auto&& item : mAnimators )
128 item->SetDisconnectAction( action );
133 void Animation::SetPlayRange( const Vector2& range )
137 // Make sure mElapsedSeconds is within the new range
139 if( mState == Stopped )
141 // Ensure that the animation starts at the right place
142 mElapsedSeconds = mPlayRange.x * mDurationSeconds;
146 // If already past the end of the range, but before end of duration, then clamp will
147 // ensure that the animation stops on the next update.
148 // If not yet at the start of the range, clamping will jump to the start
149 mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
153 void Animation::Play()
155 // Sort according to end time with earlier end times coming first, if the end time is the same, then the animators are not moved
156 std::stable_sort( mAnimators.Begin(), mAnimators.End(), CompareAnimatorEndTimes );
160 if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
162 mElapsedSeconds = mPlayRange.y * mDurationSeconds;
165 SetAnimatorsActive( true );
170 void Animation::PlayFrom( float progress )
172 // If the animation is already playing this has no effect
173 // Progress is guaranteed to be in range.
174 if( mState != Playing )
176 mElapsedSeconds = progress * mDurationSeconds;
179 SetAnimatorsActive( true );
185 void Animation::PlayAfter( float delaySeconds )
187 if( mState != Playing )
189 mDelaySeconds = delaySeconds;
192 if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
194 mElapsedSeconds = mPlayRange.y * mDurationSeconds;
197 SetAnimatorsActive( true );
203 void Animation::Pause()
205 if (mState == Playing)
211 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
213 if( action == Dali::Animation::BAKE_FINAL )
215 if( mSpeedFactor > 0.0f )
217 mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
221 mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
225 UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
228 void Animation::SetAnimatorsActive( bool active )
230 for ( auto&& item : mAnimators )
232 item->SetActive( active );
236 bool Animation::Stop(BufferIndex bufferIndex)
238 bool animationFinished(false);
240 if (mState == Playing || mState == Paused)
242 animationFinished = true; // The actor-thread should be notified of this
244 if( mEndAction != Dali::Animation::DISCARD )
246 Bake( bufferIndex, mEndAction );
248 // Animators are automatically set to inactive in Bake
252 SetAnimatorsActive( false );
255 // The animation has now been played to completion
260 mElapsedSeconds = mPlayRange.x*mDurationSeconds;
263 return animationFinished;
266 void Animation::OnDestroy(BufferIndex bufferIndex)
268 if (mState == Playing || mState == Paused)
270 if (mEndAction != Dali::Animation::DISCARD)
272 Bake( bufferIndex, mEndAction );
274 // Animators are automatically set to inactive in Bake
278 SetAnimatorsActive( false );
285 void Animation::SetLoopingMode( bool loopingMode )
287 mAutoReverseEnabled = loopingMode;
289 for ( auto&& item : mAnimators )
291 // Send some variables together to figure out the Animation status
292 item->SetSpeedFactor( mSpeedFactor );
293 item->SetLoopCount( mLoopCount );
294 item->SetLoopingMode( loopingMode );
298 void Animation::AddAnimator( OwnerPointer<AnimatorBase>& animator )
300 animator->ConnectToSceneGraph();
301 animator->SetDisconnectAction( mDisconnectAction );
303 mAnimators.PushBack( animator.Release() );
306 void Animation::Update( BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
311 if (mState == Stopped || mState == Destroyed)
313 // Short circuit when animation isn't running
317 // The animation must still be applied when Paused/Stopping
318 if (mState == Playing)
320 // Sign value of speed factor. It can optimize many arithmetic comparision
321 float signSpeedFactor = ( mSpeedFactor < 0.0f ) ? -1.f : 1.f;
323 // If there is delay time before Animation starts, wait the Animation until mDelaySeconds.
324 if( mDelaySeconds > 0.0f )
326 float reduceSeconds = fabsf( elapsedSeconds * mSpeedFactor );
327 if( reduceSeconds > mDelaySeconds )
329 // add overflowed time to mElapsedSecond.
330 // If speed factor > 0, add it. if speed factor < 0, subtract it.
331 float overflowSeconds = reduceSeconds - mDelaySeconds;
332 mElapsedSeconds += signSpeedFactor * overflowSeconds;
333 mDelaySeconds = 0.0f;
337 mDelaySeconds -= reduceSeconds;
342 mElapsedSeconds += ( elapsedSeconds * mSpeedFactor );
345 const float playRangeStartSeconds = mPlayRange.x * mDurationSeconds;
346 const float playRangeEndSeconds = mPlayRange.y * mDurationSeconds;
347 // Final reached seconds. It can optimize many arithmetic comparision
348 float edgeRangeSeconds = ( mSpeedFactor < 0.0f ) ? playRangeStartSeconds : playRangeEndSeconds;
350 // Optimized Factors.
351 // elapsed > edge --> check if looped
352 // elapsed >= marker --> check if elapsed reached to marker in normal case
353 // edge >= marker --> check if elapsed reached to marker in looped case
354 float elapsedFactor = signSpeedFactor * mElapsedSeconds;
355 float edgeFactor = signSpeedFactor * edgeRangeSeconds;
356 float markerFactor = signSpeedFactor * mProgressMarker;
358 // check it is looped
359 looped = ( elapsedFactor > edgeFactor );
363 WrapInPlayRange( mElapsedSeconds, playRangeStartSeconds, playRangeEndSeconds );
365 // Recalculate elapsedFactor here
366 elapsedFactor = signSpeedFactor * mElapsedSeconds;
368 if( mLoopCount != 0 )
370 // Check If this animation is finished
372 if( mCurrentLoop >= mLoopCount )
374 DALI_ASSERT_DEBUG( mCurrentLoop == mLoopCount );
377 // The animation has now been played to completion
380 // Make elapsed second as edge of range forcely.
381 mElapsedSeconds = edgeRangeSeconds + signSpeedFactor * Math::MACHINE_EPSILON_10;
382 UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::DISCARD), finished );
384 // After update animation, mElapsedSeconds must be begin of value
385 mElapsedSeconds = playRangeStartSeconds + playRangeEndSeconds - edgeRangeSeconds;
390 // when it is on looped state, 2 case to send progress signal.
391 // (require && range_value >= marker) || << Signal at previous loop
392 // (marker > 0 && !finished && elaped >= marker) << Signal at current loop
393 if( ( mProgressMarker > 0.0f ) && !finished && ( elapsedFactor >= markerFactor ) )
395 // The application should be notified by NotificationManager, in another thread
396 progressReached = true;
397 mProgressReachedSignalRequired = false;
401 if( mProgressReachedSignalRequired && ( edgeFactor >= markerFactor ) )
403 progressReached = true;
405 mProgressReachedSignalRequired = mProgressMarker > 0.0f;
410 // when it is not on looped state, only 1 case to send progress signal.
411 // (require && elaped >= marker)
412 if( mProgressReachedSignalRequired && ( elapsedFactor >= markerFactor ) )
414 // The application should be notified by NotificationManager, in another thread
415 progressReached = true;
416 mProgressReachedSignalRequired = false;
421 // Already updated when finished. So skip.
424 UpdateAnimators(bufferIndex, false, false );
428 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
430 mIsActive[bufferIndex] = false;
432 const Vector2 playRange( mPlayRange * mDurationSeconds );
433 float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
435 //Loop through all animators
437 for ( auto&& iter = mAnimators.Begin(); iter != mAnimators.End(); )
439 AnimatorBase *animator = *iter;
441 if( animator->Orphan() )
443 //Remove animators whose PropertyOwner has been destroyed
444 iter = mAnimators.Erase(iter);
448 if( animator->IsEnabled() )
450 const float intervalDelay( animator->GetIntervalDelay() );
452 if( elapsedSecondsClamped >= intervalDelay )
454 // Calculate a progress specific to each individual animator
455 float progress(1.0f);
456 const float animatorDuration = animator->GetDuration();
457 if (animatorDuration > 0.0f) // animators can be "immediate"
459 progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f , 1.0f );
461 animator->Update(bufferIndex, progress, bake);
463 if (animatorDuration > 0.0f && (elapsedSecondsClamped - intervalDelay) <= animatorDuration)
465 mIsActive[bufferIndex] = true;
475 if ( animationFinished )
477 animator->SetActive( false );
482 INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
491 } // namespace SceneGraph
493 } // namespace Internal