[dali_1.2.44] 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
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->GetInitialDelay() + lhs->GetDuration() ) < ( rhs->GetInitialDelay() + 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 : mDurationSeconds(durationSeconds),
69   mSpeedFactor( speedFactor ),
70   mEndAction(endAction),
71   mDisconnectAction(disconnectAction),
72   mState(Stopped),
73   mElapsedSeconds(playRange.x*mDurationSeconds),
74   mPlayedCount(0),
75   mLoopCount(loopCount),
76   mCurrentLoop(0),
77   mPlayRange( playRange ),
78   mProgressMarker(0.0f),
79   mProgressReachedSignalRequired( false )
80 {
81 }
82
83 Animation::~Animation()
84 {
85 }
86
87 void Animation::operator delete( void* ptr )
88 {
89   gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
90 }
91
92 void Animation::SetDuration(float durationSeconds)
93 {
94   mDurationSeconds = durationSeconds;
95 }
96
97 void Animation::SetProgressNotification( float progress )
98 {
99   mProgressMarker = progress;
100   if ( mProgressMarker > 0.0f )
101   {
102     mProgressReachedSignalRequired = true;
103   }
104 }
105
106 void Animation::SetLoopCount(int loopCount)
107 {
108   mLoopCount = loopCount;
109   mCurrentLoop = 0;
110 }
111
112 void Animation::SetEndAction(Dali::Animation::EndAction action)
113 {
114   mEndAction = action;
115 }
116
117 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
118 {
119   if ( mDisconnectAction != action )
120   {
121     mDisconnectAction = action;
122
123     for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
124     {
125       (*iter)->SetDisconnectAction( action );
126     }
127   }
128 }
129
130 void Animation::SetPlayRange( const Vector2& range )
131 {
132   mPlayRange = range;
133
134   // Make sure mElapsedSeconds is within the new range
135
136   if( mState == Stopped )
137   {
138     // Ensure that the animation starts at the right place
139     mElapsedSeconds = mPlayRange.x * mDurationSeconds;
140   }
141   else
142   {
143     // If already past the end of the range, but before end of duration, then clamp will
144     // ensure that the animation stops on the next update.
145     // If not yet at the start of the range, clamping will jump to the start
146     mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
147   }
148 }
149
150 void Animation::Play()
151 {
152   // Sort according to end time with earlier end times coming first, if the end time is the same, then the animators are not moved
153   std::stable_sort( mAnimators.Begin(), mAnimators.End(), CompareAnimatorEndTimes );
154
155   mState = Playing;
156
157   if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
158   {
159     mElapsedSeconds = mPlayRange.y * mDurationSeconds;
160   }
161
162   SetAnimatorsActive( true );
163
164   mCurrentLoop = 0;
165 }
166
167 void Animation::PlayFrom( float progress )
168 {
169   // If the animation is already playing this has no effect
170   // Progress is guaranteed to be in range.
171   if( mState != Playing )
172   {
173     mElapsedSeconds = progress * mDurationSeconds;
174     mState = Playing;
175
176     SetAnimatorsActive( true );
177   }
178 }
179
180 void Animation::Pause()
181 {
182   if (mState == Playing)
183   {
184     mState = Paused;
185   }
186 }
187
188 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
189 {
190   if( action == Dali::Animation::BakeFinal )
191   {
192     if( mSpeedFactor > 0.0f )
193     {
194       mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
195     }
196     else
197     {
198       mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
199     }
200   }
201
202   UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
203 }
204
205 void Animation::SetAnimatorsActive( bool active )
206 {
207   for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
208   {
209     (*iter)->SetActive( active );
210   }
211 }
212
213 bool Animation::Stop(BufferIndex bufferIndex)
214 {
215   bool animationFinished(false);
216
217   if (mState == Playing || mState == Paused)
218   {
219     animationFinished = true; // The actor-thread should be notified of this
220
221     if( mEndAction != Dali::Animation::Discard )
222     {
223       Bake( bufferIndex, mEndAction );
224
225       // Animators are automatically set to inactive in Bake
226     }
227     else
228     {
229       SetAnimatorsActive( false );
230     }
231
232     // The animation has now been played to completion
233     ++mPlayedCount;
234     mCurrentLoop = 0;
235   }
236
237   mElapsedSeconds = mPlayRange.x*mDurationSeconds;
238   mState = Stopped;
239
240   return animationFinished;
241 }
242
243 void Animation::OnDestroy(BufferIndex bufferIndex)
244 {
245   if (mState == Playing || mState == Paused)
246   {
247     if (mEndAction != Dali::Animation::Discard)
248     {
249       Bake( bufferIndex, mEndAction );
250
251       // Animators are automatically set to inactive in Bake
252     }
253     else
254     {
255       SetAnimatorsActive( false );
256     }
257   }
258
259   mState = Destroyed;
260 }
261
262 void Animation::AddAnimator( OwnerPointer<AnimatorBase>& animator )
263 {
264   animator->ConnectToSceneGraph();
265   animator->SetDisconnectAction( mDisconnectAction );
266
267   mAnimators.PushBack( animator.Release() );
268 }
269
270 void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
271 {
272   looped = false;
273   finished = false;
274
275   if (mState == Stopped || mState == Destroyed)
276   {
277     // Short circuit when animation isn't running
278     return;
279   }
280
281   // The animation must still be applied when Paused/Stopping
282   if (mState == Playing)
283   {
284     mElapsedSeconds += elapsedSeconds * mSpeedFactor;
285
286     if ( mProgressReachedSignalRequired && ( mElapsedSeconds >= mProgressMarker ) )
287     {
288       // The application should be notified by NotificationManager, in another thread
289       progressReached = true;
290       mProgressReachedSignalRequired = false;
291     }
292   }
293
294   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
295
296   if( 0 == mLoopCount )
297   {
298     // loop forever
299     WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
300
301     UpdateAnimators(bufferIndex, false, false );
302
303     // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
304   }
305   else if( mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
306   {
307     // looping
308     looped =  (mState == Playing                                                 &&
309                (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
310                 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
311
312     WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
313
314     UpdateAnimators(bufferIndex, false, false );
315
316     if(looped)
317     {
318       ++mCurrentLoop;
319       mProgressReachedSignalRequired = mProgressMarker > 0.0f;
320       // don't increment mPlayedCount until the finished final loop
321     }
322   }
323   else
324   {
325     // playing once (and last mCurrentLoop)
326     finished = (mState == Playing                                                 &&
327                 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
328                  ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
329
330     // update with bake if finished
331     UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished );
332
333     if(finished)
334     {
335       // The animation has now been played to completion
336       ++mPlayedCount;
337
338       // loop iterations come to this else branch for their final iterations
339       if( mCurrentLoop < mLoopCount)
340       {
341         ++mCurrentLoop;
342         DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
343       }
344
345       mProgressReachedSignalRequired = mProgressMarker > 0.0f;
346       mElapsedSeconds = playRangeSeconds.x;
347       mState = Stopped;
348     }
349   }
350 }
351
352 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
353 {
354   const Vector2 playRange( mPlayRange * mDurationSeconds );
355   float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
356
357   //Loop through all animators
358   bool applied(true);
359   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
360   {
361     AnimatorBase *animator = *iter;
362
363     if( animator->Orphan() )
364     {
365       //Remove animators whose PropertyOwner has been destroyed
366       iter = mAnimators.Erase(iter);
367     }
368     else
369     {
370       if( animator->IsEnabled() )
371       {
372         const float initialDelay( animator->GetInitialDelay() );
373         if( elapsedSecondsClamped >= initialDelay )
374         {
375           // Calculate a progress specific to each individual animator
376           float progress(1.0f);
377           const float animatorDuration = animator->GetDuration();
378           if (animatorDuration > 0.0f) // animators can be "immediate"
379           {
380             progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
381           }
382           animator->Update(bufferIndex, progress, bake);
383         }
384         applied = true;
385       }
386       else
387       {
388         applied = false;
389       }
390
391       if ( animationFinished )
392       {
393         animator->SetActive( false );
394       }
395
396       if (applied)
397       {
398         INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
399       }
400
401       ++iter;
402     }
403   }
404
405 }
406
407 } // namespace SceneGraph
408
409 } // namespace Internal
410
411 } // namespace Dali