Merge "Add @DEPRECATED to all public symbols of ImageActor & ShaderEffect" into devel...
[platform/core/uifw/dali-core.git] / dali / internal / update / animation / scene-graph-animation.cpp
1 /*
2  * Copyright (c) 2014 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 + fmod(elapsed, playRangeSeconds.y);
38   }
39   else if( elapsed < playRangeSeconds.x )
40   {
41     elapsed = playRangeSeconds.y - fmod(elapsed, playRangeSeconds.y);
42   }
43 }
44
45 }
46
47 namespace Dali
48 {
49
50 namespace Internal
51 {
52
53 namespace SceneGraph
54 {
55
56 Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction )
57 {
58   return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, loopCount, endAction, disconnectAction );
59 }
60
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),
66   mState(Stopped),
67   mElapsedSeconds(playRange.x*mDurationSeconds),
68   mPlayedCount(0),
69   mLoopCount(loopCount),
70   mCurrentLoop(0),
71   mPlayRange( playRange )
72 {
73 }
74
75 Animation::~Animation()
76 {
77 }
78
79 void Animation::operator delete( void* ptr )
80 {
81   gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
82 }
83
84 void Animation::SetDuration(float durationSeconds)
85 {
86   mDurationSeconds = durationSeconds;
87 }
88
89 void Animation::SetLoopCount(int loopCount)
90 {
91   mLoopCount = loopCount;
92   mCurrentLoop = 0;
93 }
94
95 void Animation::SetEndAction(Dali::Animation::EndAction action)
96 {
97   mEndAction = action;
98 }
99
100 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
101 {
102   if ( mDisconnectAction != action )
103   {
104     mDisconnectAction = action;
105
106     for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
107     {
108       (*iter)->SetDisconnectAction( action );
109     }
110   }
111 }
112
113 void Animation::SetPlayRange( const Vector2& range )
114 {
115   mPlayRange = range;
116
117   //Make sure mElapsedSeconds is within the new range
118   mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
119 }
120
121 void Animation::Play()
122 {
123   mState = Playing;
124
125   if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
126   {
127     mElapsedSeconds = mPlayRange.y * mDurationSeconds;
128   }
129
130   SetAnimatorsActive( true );
131
132   mCurrentLoop = 0;
133 }
134
135 void Animation::PlayFrom( float progress )
136 {
137   //If the animation is already playing this has no effect
138   if( mState != Playing )
139   {
140     mElapsedSeconds = progress * mDurationSeconds;
141     mState = Playing;
142
143     SetAnimatorsActive( true );
144   }
145 }
146
147 void Animation::Pause()
148 {
149   if (mState == Playing)
150   {
151     mState = Paused;
152   }
153 }
154
155 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
156 {
157   if( action == Dali::Animation::BakeFinal )
158   {
159     if( mSpeedFactor > 0.0f )
160     {
161       mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
162     }
163     else
164     {
165       mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
166     }
167   }
168
169   UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
170 }
171
172 void Animation::SetAnimatorsActive( bool active )
173 {
174   for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
175   {
176     (*iter)->SetActive( active );
177   }
178 }
179
180 bool Animation::Stop(BufferIndex bufferIndex)
181 {
182   bool animationFinished(false);
183
184   if (mState == Playing || mState == Paused)
185   {
186     animationFinished = true; // The actor-thread should be notified of this
187
188     if( mEndAction != Dali::Animation::Discard )
189     {
190       Bake( bufferIndex, mEndAction );
191
192       // Animators are automatically set to inactive in Bake
193     }
194     else
195     {
196       SetAnimatorsActive( false );
197     }
198
199     // The animation has now been played to completion
200     ++mPlayedCount;
201     mCurrentLoop = 0;
202   }
203
204   mElapsedSeconds = mPlayRange.x*mDurationSeconds;
205   mState = Stopped;
206
207   return animationFinished;
208 }
209
210 void Animation::OnDestroy(BufferIndex bufferIndex)
211 {
212   if (mState == Playing || mState == Paused)
213   {
214     if (mEndAction != Dali::Animation::Discard)
215     {
216       Bake( bufferIndex, mEndAction );
217
218       // Animators are automatically set to inactive in Bake
219     }
220     else
221     {
222       SetAnimatorsActive( false );
223     }
224   }
225
226   mState = Destroyed;
227 }
228
229 void Animation::AddAnimator( AnimatorBase* animator )
230 {
231   animator->ConnectToSceneGraph();
232   animator->SetDisconnectAction( mDisconnectAction );
233
234   mAnimators.PushBack( animator );
235 }
236
237 void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished )
238 {
239   looped = false;
240   finished = false;
241
242   if (mState == Stopped || mState == Destroyed)
243   {
244     // Short circuit when animation isn't running
245     return;
246   }
247
248   // The animation must still be applied when Paused/Stopping
249   if (mState == Playing)
250   {
251     mElapsedSeconds += elapsedSeconds * mSpeedFactor;
252   }
253
254   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
255
256   if( 0 == mLoopCount )
257   {
258     // loop forever
259     WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
260
261     UpdateAnimators(bufferIndex, false, false);
262
263     // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
264   }
265   else if( mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
266   {
267     // looping
268     looped =  (mState == Playing                                                 &&
269                (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
270                 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
271
272     WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
273
274     UpdateAnimators(bufferIndex, false, false);
275
276     if(looped)
277     {
278       ++mCurrentLoop;
279       // don't increment mPlayedCount until the finished final loop
280     }
281   }
282   else
283   {
284     // playing once (and last mCurrentLoop)
285     finished = (mState == Playing                                                 &&
286                 (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
287                  ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
288
289     // update with bake if finished
290     UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished);
291
292     if(finished)
293     {
294       // The animation has now been played to completion
295       ++mPlayedCount;
296
297       // loop iterations come to this else branch for their final iterations
298       if( mCurrentLoop < mLoopCount)
299       {
300         ++mCurrentLoop;
301         DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
302       }
303
304       mElapsedSeconds = playRangeSeconds.x;
305       mState = Stopped;
306     }
307   }
308
309 }
310
311 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
312 {
313   float elapsedSecondsClamped = Clamp( mElapsedSeconds, mPlayRange.x * mDurationSeconds,mPlayRange.y * mDurationSeconds );
314
315   //Loop through all animators
316   bool applied(true);
317   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
318   {
319     AnimatorBase *animator = *iter;
320
321     if( animator->Orphan() )
322     {
323       //Remove animators whose PropertyOwner has been destroyed
324       iter = mAnimators.Erase(iter);
325     }
326     else
327     {
328       if( animator->IsEnabled() )
329       {
330         const float initialDelay(animator->GetInitialDelay());
331         if (elapsedSecondsClamped >= initialDelay || mSpeedFactor < 0.0f )
332         {
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"
337           {
338             progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
339           }
340           animator->Update(bufferIndex, progress, bake);
341         }
342         applied = true;
343       }
344       else
345       {
346         applied = false;
347       }
348
349       if ( animationFinished )
350       {
351         animator->SetActive( false );
352       }
353
354       if (applied)
355       {
356         INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
357       }
358
359       ++iter;
360     }
361   }
362
363 }
364
365 } // namespace SceneGraph
366
367 } // namespace Internal
368
369 } // namespace Dali