[dali_1.2.52] Merge branch 'devel/master'
[platform/core/uifw/dali-core.git] / dali / internal / update / animation / scene-graph-animation.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/update/animation/scene-graph-animation.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath> // fmod
23
24 // INTERNAL INCLUDES
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
29 {
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;
32
33 inline void WrapInPlayRange( float& elapsed, const Dali::Vector2& playRangeSeconds)
34 {
35   if( elapsed > playRangeSeconds.y )
36   {
37     elapsed = playRangeSeconds.x + fmodf((elapsed-playRangeSeconds.x), (playRangeSeconds.y-playRangeSeconds.x));
38   }
39   else if( elapsed < playRangeSeconds.x )
40   {
41     elapsed = playRangeSeconds.y - fmodf( (playRangeSeconds.x - elapsed), (playRangeSeconds.y-playRangeSeconds.x) );
42   }
43 }
44
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 )
47 {
48   return ( ( lhs->GetIntervalDelay() + lhs->GetDuration() ) < ( rhs->GetIntervalDelay() + rhs->GetDuration() ) );
49 }
50
51 } // unnamed namespace
52
53 namespace Dali
54 {
55
56 namespace Internal
57 {
58
59 namespace SceneGraph
60 {
61
62 Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction )
63 {
64   return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, loopCount, endAction, disconnectAction );
65 }
66
67 Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int 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 ),
74   mPlayedCount( 0 ),
75   mLoopCount(loopCount),
76   mCurrentLoop(0),
77   mEndAction(endAction),
78   mDisconnectAction(disconnectAction),
79   mState(Stopped),
80   mProgressReachedSignalRequired( false ),
81   mAutoReverseEnabled( false )
82 {
83 }
84
85 Animation::~Animation()
86 {
87 }
88
89 void Animation::operator delete( void* ptr )
90 {
91   gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
92 }
93
94 void Animation::SetDuration(float durationSeconds)
95 {
96   mDurationSeconds = durationSeconds;
97 }
98
99 void Animation::SetProgressNotification( float progress )
100 {
101   mProgressMarker = progress;
102   if ( mProgressMarker > 0.0f )
103   {
104     mProgressReachedSignalRequired = true;
105   }
106 }
107
108 void Animation::SetLoopCount(int loopCount)
109 {
110   mLoopCount = loopCount;
111   mCurrentLoop = 0;
112 }
113
114 void Animation::SetEndAction(Dali::Animation::EndAction action)
115 {
116   mEndAction = action;
117 }
118
119 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
120 {
121   if ( mDisconnectAction != action )
122   {
123     mDisconnectAction = action;
124
125     for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
126     {
127       (*iter)->SetDisconnectAction( action );
128     }
129   }
130 }
131
132 void Animation::SetPlayRange( const Vector2& range )
133 {
134   mPlayRange = range;
135
136   // Make sure mElapsedSeconds is within the new range
137
138   if( mState == Stopped )
139   {
140     // Ensure that the animation starts at the right place
141     mElapsedSeconds = mPlayRange.x * mDurationSeconds;
142   }
143   else
144   {
145     // If already past the end of the range, but before end of duration, then clamp will
146     // ensure that the animation stops on the next update.
147     // If not yet at the start of the range, clamping will jump to the start
148     mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
149   }
150 }
151
152 void Animation::Play()
153 {
154   // Sort according to end time with earlier end times coming first, if the end time is the same, then the animators are not moved
155   std::stable_sort( mAnimators.Begin(), mAnimators.End(), CompareAnimatorEndTimes );
156
157   mState = Playing;
158
159   if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
160   {
161     mElapsedSeconds = mPlayRange.y * mDurationSeconds;
162   }
163
164   SetAnimatorsActive( true );
165
166   mCurrentLoop = 0;
167 }
168
169 void Animation::PlayFrom( float progress )
170 {
171   // If the animation is already playing this has no effect
172   // Progress is guaranteed to be in range.
173   if( mState != Playing )
174   {
175     mElapsedSeconds = progress * mDurationSeconds;
176     mState = Playing;
177
178     SetAnimatorsActive( true );
179   }
180 }
181
182 void Animation::PlayAfter( float delaySeconds )
183 {
184   if( mState != Playing )
185   {
186     mDelaySeconds = delaySeconds;
187     mState = Playing;
188
189     if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
190     {
191       mElapsedSeconds = mPlayRange.y * mDurationSeconds;
192     }
193
194     SetAnimatorsActive( true );
195
196     mCurrentLoop = 0;
197   }
198 }
199
200 void Animation::Pause()
201 {
202   if (mState == Playing)
203   {
204     mState = Paused;
205   }
206 }
207
208 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
209 {
210   if( action == Dali::Animation::BakeFinal )
211   {
212     if( mSpeedFactor > 0.0f )
213     {
214       mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
215     }
216     else
217     {
218       mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
219     }
220   }
221
222   UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
223 }
224
225 void Animation::SetAnimatorsActive( bool active )
226 {
227   for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
228   {
229     (*iter)->SetActive( active );
230   }
231 }
232
233 bool Animation::Stop(BufferIndex bufferIndex)
234 {
235   bool animationFinished(false);
236
237   if (mState == Playing || mState == Paused)
238   {
239     animationFinished = true; // The actor-thread should be notified of this
240
241     if( mEndAction != Dali::Animation::Discard )
242     {
243       Bake( bufferIndex, mEndAction );
244
245       // Animators are automatically set to inactive in Bake
246     }
247     else
248     {
249       SetAnimatorsActive( false );
250     }
251
252     // The animation has now been played to completion
253     ++mPlayedCount;
254     mCurrentLoop = 0;
255   }
256
257   mElapsedSeconds = mPlayRange.x*mDurationSeconds;
258   mState = Stopped;
259
260   return animationFinished;
261 }
262
263 void Animation::OnDestroy(BufferIndex bufferIndex)
264 {
265   if (mState == Playing || mState == Paused)
266   {
267     if (mEndAction != Dali::Animation::Discard)
268     {
269       Bake( bufferIndex, mEndAction );
270
271       // Animators are automatically set to inactive in Bake
272     }
273     else
274     {
275       SetAnimatorsActive( false );
276     }
277   }
278
279   mState = Destroyed;
280 }
281
282 void Animation::SetLoopingMode( bool loopingMode )
283 {
284   mAutoReverseEnabled = loopingMode;
285
286   for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
287   {
288     // Send some variables together to figure out the Animation status
289     (*iter)->SetSpeedFactor( mSpeedFactor );
290     (*iter)->SetLoopCount( mLoopCount );
291
292     (*iter)->SetLoopingMode( loopingMode );
293   }
294 }
295
296 void Animation::AddAnimator( OwnerPointer<AnimatorBase>& animator )
297 {
298   animator->ConnectToSceneGraph();
299   animator->SetDisconnectAction( mDisconnectAction );
300
301   mAnimators.PushBack( animator.Release() );
302 }
303
304 void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
305 {
306   looped = false;
307   finished = false;
308
309   if (mState == Stopped || mState == Destroyed)
310   {
311     // Short circuit when animation isn't running
312     return;
313   }
314
315   // The animation must still be applied when Paused/Stopping
316   if (mState == Playing)
317   {
318     // If there is delay time before Animation starts, wait the Animation until mDelaySeconds.
319     if( mDelaySeconds > 0)
320     {
321       mDelaySeconds = mDelaySeconds - ( elapsedSeconds * mSpeedFactor );
322     }
323     else
324     {
325       mElapsedSeconds += elapsedSeconds * mSpeedFactor;
326
327       if ( mProgressReachedSignalRequired && ( mElapsedSeconds >= mProgressMarker ) )
328       {
329         // The application should be notified by NotificationManager, in another thread
330         progressReached = true;
331         mProgressReachedSignalRequired = false;
332       }
333     }
334   }
335
336   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
337
338   if( 0 == mLoopCount )
339   {
340     // loop forever
341     WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
342
343     UpdateAnimators(bufferIndex, false, false );
344
345     // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
346   }
347   else if( mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
348   {
349     // looping
350     looped =  (mState == Playing                                                 &&
351                (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
352                 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
353
354     WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
355
356     UpdateAnimators(bufferIndex, false, false );
357
358     if(looped)
359     {
360       ++mCurrentLoop;
361       mProgressReachedSignalRequired = mProgressMarker > 0.0f;
362       // don't increment mPlayedCount until the finished final loop
363     }
364   }
365   else
366   {
367     // playing once (and last mCurrentLoop)
368     finished = (mState == Playing                                                 &&
369                 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
370                  ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
371
372     // update with bake if finished
373     UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished );
374
375     if(finished)
376     {
377       // The animation has now been played to completion
378       ++mPlayedCount;
379
380       // loop iterations come to this else branch for their final iterations
381       if( mCurrentLoop < mLoopCount)
382       {
383         ++mCurrentLoop;
384         DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
385       }
386
387       mProgressReachedSignalRequired = mProgressMarker > 0.0f;
388       mElapsedSeconds = playRangeSeconds.x;
389       mState = Stopped;
390     }
391   }
392 }
393
394 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
395 {
396   const Vector2 playRange( mPlayRange * mDurationSeconds );
397   float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
398
399   //Loop through all animators
400   bool applied(true);
401   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
402   {
403     AnimatorBase *animator = *iter;
404
405     if( animator->Orphan() )
406     {
407       //Remove animators whose PropertyOwner has been destroyed
408       iter = mAnimators.Erase(iter);
409     }
410     else
411     {
412       if( animator->IsEnabled() )
413       {
414         const float intervalDelay( animator->GetIntervalDelay() );
415
416         if( elapsedSecondsClamped >= intervalDelay )
417         {
418           // Calculate a progress specific to each individual animator
419           float progress(1.0f);
420           const float animatorDuration = animator->GetDuration();
421           if (animatorDuration > 0.0f) // animators can be "immediate"
422           {
423             progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f , 1.0f );
424           }
425           animator->Update(bufferIndex, progress, bake);
426         }
427         applied = true;
428       }
429       else
430       {
431         applied = false;
432       }
433
434       if ( animationFinished )
435       {
436         animator->SetActive( false );
437       }
438
439       if (applied)
440       {
441         INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
442       }
443
444       ++iter;
445     }
446   }
447
448 }
449
450 } // namespace SceneGraph
451
452 } // namespace Internal
453
454 } // namespace Dali