Merge "BufferImage mirrors an external Pixel Buffer on creation." into devel/master
[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/render/common/performance-monitor.h>
26
27 namespace Dali
28 {
29
30 namespace Internal
31 {
32
33 namespace SceneGraph
34 {
35
36
37 Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
38 : mDurationSeconds(durationSeconds),
39   mSpeedFactor( speedFactor ),
40   mLooping(isLooping),
41   mEndAction(endAction),
42   mDisconnectAction(disconnectAction),
43   mState(Stopped),
44   mElapsedSeconds(playRange.x*mDurationSeconds),
45   mPlayCount(0),
46   mPlayRange( playRange )
47 {
48 }
49
50 Animation::~Animation()
51 {
52 }
53
54 void Animation::SetDuration(float durationSeconds)
55 {
56   mDurationSeconds = durationSeconds;
57 }
58
59 void Animation::SetLooping(bool looping)
60 {
61   mLooping = looping;
62 }
63
64 void Animation::SetEndAction(Dali::Animation::EndAction action)
65 {
66   mEndAction = action;
67 }
68
69 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
70 {
71   if ( mDisconnectAction != action )
72   {
73     mDisconnectAction = action;
74
75     for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
76     {
77       (*iter)->SetDisconnectAction( action );
78     }
79   }
80 }
81
82 void Animation::SetPlayRange( const Vector2& range )
83 {
84   mPlayRange = range;
85
86   //Make sure mElapsedSeconds is within the new range
87   mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
88 }
89
90 void Animation::Play()
91 {
92   mState = Playing;
93
94   if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
95   {
96     mElapsedSeconds = mPlayRange.y * mDurationSeconds;
97   }
98
99   SetAnimatorsActive( true );
100 }
101
102 void Animation::PlayFrom( float progress )
103 {
104   //If the animation is already playing this has no effect
105   if( mState != Playing )
106   {
107     mElapsedSeconds = progress * mDurationSeconds;
108     mState = Playing;
109
110     SetAnimatorsActive( true );
111   }
112 }
113
114 void Animation::Pause()
115 {
116   if (mState == Playing)
117   {
118     mState = Paused;
119   }
120 }
121
122 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
123 {
124   if( action == Dali::Animation::BakeFinal )
125   {
126     if( mSpeedFactor > 0.0f )
127     {
128       mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
129     }
130     else
131     {
132       mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
133     }
134   }
135
136   UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
137 }
138
139 void Animation::SetAnimatorsActive( bool active )
140 {
141   for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
142   {
143     (*iter)->SetActive( active );
144   }
145 }
146
147 bool Animation::Stop(BufferIndex bufferIndex)
148 {
149   bool animationFinished(false);
150
151   if (mState == Playing || mState == Paused)
152   {
153     animationFinished = true; // The actor-thread should be notified of this
154
155     if( mEndAction != Dali::Animation::Discard )
156     {
157       Bake( bufferIndex, mEndAction );
158
159       // Animators are automatically set to inactive in Bake
160     }
161     else
162     {
163       SetAnimatorsActive( false );
164     }
165
166     // The animation has now been played to completion
167     ++mPlayCount;
168   }
169
170   mElapsedSeconds = mPlayRange.x*mDurationSeconds;
171   mState = Stopped;
172
173   return animationFinished;
174 }
175
176 void Animation::OnDestroy(BufferIndex bufferIndex)
177 {
178   if (mState == Playing || mState == Paused)
179   {
180     if (mEndAction != Dali::Animation::Discard)
181     {
182       Bake( bufferIndex, mEndAction );
183
184       // Animators are automatically set to inactive in Bake
185     }
186     else
187     {
188       SetAnimatorsActive( false );
189     }
190   }
191
192   mState = Destroyed;
193 }
194
195 void Animation::AddAnimator( AnimatorBase* animator )
196 {
197   animator->ConnectToSceneGraph();
198   animator->SetDisconnectAction( mDisconnectAction );
199
200   mAnimators.PushBack( animator );
201 }
202
203 bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
204 {
205   if (mState == Stopped || mState == Destroyed)
206   {
207     // Short circuit when animation isn't running
208     return false;
209   }
210
211   // The animation must still be applied when Paused/Stopping
212   if (mState == Playing)
213   {
214     mElapsedSeconds += elapsedSeconds * mSpeedFactor;
215   }
216
217   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
218   if (mLooping)
219   {
220     if (mElapsedSeconds > playRangeSeconds.y )
221     {
222       mElapsedSeconds = playRangeSeconds.x + fmod(mElapsedSeconds, playRangeSeconds.y);
223     }
224     else if( mElapsedSeconds < playRangeSeconds.x )
225     {
226       mElapsedSeconds = playRangeSeconds.y - fmod(mElapsedSeconds, playRangeSeconds.y);
227     }
228   }
229
230   const bool animationFinished(mState == Playing                                                &&
231                               (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
232                                ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x ))
233                               );
234
235   UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard), animationFinished);
236
237   if (animationFinished)
238   {
239     // The animation has now been played to completion
240     ++mPlayCount;
241
242     mElapsedSeconds = playRangeSeconds.x;
243     mState = Stopped;
244   }
245
246   return animationFinished;
247 }
248
249 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
250 {
251   float elapsedSecondsClamped = Clamp( mElapsedSeconds, mPlayRange.x * mDurationSeconds,mPlayRange.y * mDurationSeconds );
252
253   //Loop through all animators
254   bool applied(true);
255   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
256   {
257     AnimatorBase *animator = *iter;
258
259     if( animator->Orphan() )
260     {
261       //Remove animators whose PropertyOwner has been destroyed
262       iter = mAnimators.Erase(iter);
263     }
264     else
265     {
266       if( animator->IsEnabled() )
267       {
268         const float initialDelay(animator->GetInitialDelay());
269         if (elapsedSecondsClamped >= initialDelay || mSpeedFactor < 0.0f )
270         {
271           // Calculate a progress specific to each individual animator
272           float progress(1.0f);
273           const float animatorDuration = animator->GetDuration();
274           if (animatorDuration > 0.0f) // animators can be "immediate"
275           {
276             progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
277           }
278           animator->Update(bufferIndex, progress, bake);
279         }
280         applied = true;
281       }
282       else
283       {
284         applied = false;
285       }
286
287       if ( animationFinished )
288       {
289         animator->SetActive( false );
290       }
291
292       if (applied)
293       {
294         INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
295       }
296
297       ++iter;
298     }
299   }
300
301 }
302
303 } // namespace SceneGraph
304
305 } // namespace Internal
306
307 } // namespace Dali