Added Animation loop count 39/56939/20
authorLee Morgan <Lee.morgan@partner.samsung.com>
Wed, 13 Jan 2016 16:26:46 +0000 (16:26 +0000)
committerLee Morgan <Lee.morgan@partner.samsung.com>
Fri, 29 Jan 2016 14:33:54 +0000 (14:33 +0000)
Change-Id: Ic944007d1963c22642b568f8fd7b1c78b44a4fa8

automated-tests/src/dali/utc-Dali-Animation.cpp
dali/internal/event/animation/animation-impl.cpp
dali/internal/event/animation/animation-impl.h
dali/internal/event/animation/animation-playlist.cpp
dali/internal/update/animation/scene-graph-animation.cpp
dali/internal/update/animation/scene-graph-animation.h
dali/internal/update/manager/process-render-tasks.cpp
dali/internal/update/manager/update-manager.cpp
dali/public-api/animation/animation.cpp
dali/public-api/animation/animation.h

index 40e6551..fec88e8 100644 (file)
@@ -336,6 +336,422 @@ int UtcDaliAnimationSetLoopingP(void)
   END_TEST;
 }
 
+int UtcDaliAnimationSetLoopCountP(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+  Vector3 targetPosition(10.0f, 10.0f, 10.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+
+  // Start the animation
+  animation.SetLoopCount(3);
+  DALI_TEST_CHECK(animation.IsLooping());
+  animation.Play();
+
+  bool signalReceived(false);
+  AnimationFinishCheck finishCheck(signalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+
+  // Loop
+  float intervalSeconds = 3.0f;
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  DALI_TEST_EQUALS( targetPosition, actor.GetCurrentPosition(), TEST_LOCATION );
+
+  finishCheck.Reset();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  END_TEST;
+}
+
+int UtcDaliAnimationSetLoopCountP2(void)
+{
+  TestApplication application;
+
+  //
+  // switching between forever and loop count
+  //
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+  Vector3 targetPosition(10.0f, 10.0f, 10.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+  animation.SetEndAction(Animation::Discard);
+
+  // Start the animation
+  animation.SetLoopCount(3);
+  DALI_TEST_CHECK(animation.IsLooping());
+  animation.Play();
+
+  bool signalReceived(false);
+  AnimationFinishCheck finishCheck(signalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  float intervalSeconds = 3.0f;
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+
+  finishCheck.Reset();
+
+  // Loop forever
+  animation.SetLooping(true);
+  DALI_TEST_CHECK(animation.IsLooping());
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  finishCheck.Reset();
+
+  // Loop N again
+  animation.SetLoopCount(3);
+  DALI_TEST_CHECK(animation.IsLooping());
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+
+  finishCheck.Reset();
+
+  // loop forever
+  animation.SetLooping(true);
+  DALI_TEST_CHECK(animation.IsLooping());
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  finishCheck.Reset();
+
+  // Loop N again
+  animation.SetLoopCount(3);
+  DALI_TEST_CHECK(animation.IsLooping());
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived(); // we never hit play
+
+  finishCheck.Reset();
+
+
+  END_TEST;
+}
+
+int UtcDaliAnimationSetLoopCountP3(void)
+{
+  TestApplication application;
+
+  //
+  // switching between forever and loop count
+  //
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+  Vector3 targetPosition(10.0f, 10.0f, 10.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+  animation.SetEndAction(Animation::Discard);
+
+  float intervalSeconds = 3.0f;
+
+  bool signalReceived(false);
+  AnimationFinishCheck finishCheck(signalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  // loop forever
+  animation.SetLooping(true);
+  DALI_TEST_CHECK(animation.IsLooping());
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  finishCheck.Reset();
+
+  // Loop N again
+  animation.SetLoopCount(3);
+  DALI_TEST_CHECK(animation.IsLooping());
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived(); // we never hit play
+
+  finishCheck.Reset();
+
+
+  END_TEST;
+}
+
+int UtcDaliAnimationSetLoopCountP4(void)
+{
+  TestApplication application;
+
+  //
+  // ..and play again
+  //
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+  Vector3 targetPosition(10.0f, 10.0f, 10.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+  animation.SetEndAction(Animation::Bake);
+
+  float intervalSeconds = 3.0f;
+
+  bool signalReceived(false);
+  AnimationFinishCheck finishCheck(signalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  animation.SetLoopCount(1);
+  animation.Play();
+  DALI_TEST_CHECK(!animation.IsLooping());
+
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION );
+  actor.SetProperty( Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f) );
+
+  finishCheck.Reset();
+
+  animation.Play(); // again
+  DALI_TEST_CHECK(!animation.IsLooping());
+
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliAnimationGetLoopCountP(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+  Vector3 targetPosition(10.0f, 10.0f, 10.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+
+  DALI_TEST_CHECK(1 == animation.GetLoopCount());
+
+  // Start the animation
+  animation.SetLoopCount(3);
+  DALI_TEST_CHECK(animation.IsLooping());
+  DALI_TEST_CHECK(3 == animation.GetLoopCount());
+
+  animation.Play();
+
+  application.Render(0);
+  application.SendNotification();
+
+  // Loop
+  float intervalSeconds = 3.0f;
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+
+  application.Render(0);
+  application.SendNotification();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+
+  animation.SetLoopCount(0);
+  DALI_TEST_CHECK(animation.IsLooping());
+  DALI_TEST_CHECK(0 == animation.GetLoopCount());
+
+  animation.SetLoopCount(1);
+  DALI_TEST_CHECK(!animation.IsLooping());
+  DALI_TEST_CHECK(1 == animation.GetLoopCount());
+
+  END_TEST;
+}
+
+
+int UtcDaliAnimationGetCurrentLoopP(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+  Vector3 targetPosition(10.0f, 10.0f, 10.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+
+  // Start the animation
+  animation.SetLoopCount(3);
+  DALI_TEST_CHECK(animation.IsLooping());
+  DALI_TEST_CHECK(0 == animation.GetCurrentLoop());
+  animation.Play();
+
+  bool signalReceived(false);
+  AnimationFinishCheck finishCheck(signalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  application.SendNotification();
+
+  // Loop
+  float intervalSeconds = 3.0f;
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_CHECK(2 == animation.GetCurrentLoop());
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  DALI_TEST_CHECK(3 == animation.GetCurrentLoop());
+  DALI_TEST_CHECK(animation.GetLoopCount() == animation.GetCurrentLoop());
+
+  finishCheck.Reset();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_CHECK(3 == animation.GetCurrentLoop());
+
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_CHECK(3 == animation.GetCurrentLoop());
+
+  END_TEST;
+}
+
 int UtcDaliAnimationIsLoopingP(void)
 {
   TestApplication application;
index 04e9ac9..04ca30b 100644 (file)
@@ -122,7 +122,8 @@ Animation::Animation( EventThreadServices& eventThreadServices, AnimationPlaylis
   mFinishedCallbackObject( NULL ),
   mDurationSeconds( durationSeconds ),
   mSpeedFactor(1.0f),
-  mIsLooping( false ),
+  mLoopCount(1),
+  mCurrentLoop(0),
   mPlayRange( Vector2(0.0f,1.0f)),
   mEndAction( endAction ),
   mDisconnectAction( disconnectAction ),
@@ -159,7 +160,7 @@ void Animation::CreateSceneObject()
   DALI_ASSERT_DEBUG( mAnimation == NULL );
 
   // Create a new animation, temporarily owned
-  SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mPlayRange, mIsLooping, mEndAction, mDisconnectAction );
+  SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mPlayRange, mLoopCount, mEndAction, mDisconnectAction );
 
   // Keep a const pointer to the animation.
   mAnimation = animation;
@@ -199,19 +200,33 @@ float Animation::GetDuration() const
   return mDurationSeconds;
 }
 
-void Animation::SetLooping(bool looping)
+void Animation::SetLooping(bool on)
+{
+  SetLoopCount( on ? 0 : 1 );
+}
+
+void Animation::SetLoopCount(int count)
 {
   // Cache for public getters
-  mIsLooping = looping;
+  mLoopCount = count;
 
   // mAnimation is being used in a separate thread; queue a message to set the value
-  SetLoopingMessage( mEventThreadServices, *mAnimation, looping );
+  SetLoopingMessage( mEventThreadServices, *mAnimation, mLoopCount );
+}
+
+int Animation::GetLoopCount()
+{
+  return mLoopCount;
+}
+
+int Animation::GetCurrentLoop()
+{
+  return mCurrentLoop;
 }
 
 bool Animation::IsLooping() const
 {
-  // This is not animatable; the cached value is up-to-date.
-  return mIsLooping;
+  return mLoopCount != 1;
 }
 
 void Animation::SetEndAction(EndAction action)
@@ -721,13 +736,15 @@ void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Alph
 bool Animation::HasFinished()
 {
   bool hasFinished(false);
-  const int playCount(mAnimation->GetPlayCount());
+  const int playedCount(mAnimation->GetPlayedCount());
 
   // If the play count has been incremented, then another notification is required
-  if (playCount > mNotificationCount)
+  mCurrentLoop = mAnimation->GetCurrentLoop();
+
+  if (playedCount > mNotificationCount)
   {
     // Note that only one signal is emitted, if the animation has been played repeatedly
-    mNotificationCount = playCount;
+    mNotificationCount = playedCount;
 
     hasFinished = true;
   }
index 6272042..f3ade18 100644 (file)
@@ -85,7 +85,22 @@ public:
   /**
    * @copydoc Dali::Animation::SetLooping()
    */
-  void SetLooping(bool looping);
+  void SetLooping(bool on);
+
+  /**
+   * @copydoc Dali::Animation::SetLoopCount()
+   */
+  void SetLoopCount(int count);
+
+  /**
+   * @copydoc Dali::Animation::GetLoopCount()
+   */
+  int GetLoopCount();
+
+  /**
+   * @copydoc Dali::Animation::GetCurrentLoop()
+   */
+  int GetCurrentLoop();
 
   /**
    * @copydoc Dali::Animation::IsLooping()
@@ -446,7 +461,8 @@ private:
   // Cached for public getters
   float mDurationSeconds;
   float mSpeedFactor;
-  bool mIsLooping;
+  int mLoopCount;
+  int mCurrentLoop;
   Vector2 mPlayRange;
   EndAction mEndAction;
   EndAction mDisconnectAction;
index ef3644b..99a61ca 100644 (file)
@@ -91,6 +91,7 @@ void AnimationPlaylist::NotifyCompleted()
       // The animation may be present in mPlaylist - remove if necessary
       // Note that the animation "Finish" signal is emitted after Stop() has been called
       std::vector< Dali::Animation >::iterator iter = std::find( mPlaylist.begin(), mPlaylist.end(), Dali::Animation(animation) );
+      DALI_ASSERT_DEBUG(iter != mPlaylist.end());
       mPlaylist.erase( iter );
     }
   }
index 207d5b4..877010c 100644 (file)
@@ -29,6 +29,19 @@ namespace //Unnamed namespace
 {
 //Memory pool used to allocate new animations. Memory used by this pool will be released when shutting down DALi
 Dali::Internal::MemoryPoolObjectAllocator<Dali::Internal::SceneGraph::Animation> gAnimationMemoryPool;
+
+inline void WrapInPlayRange( float& elapsed, const Dali::Vector2& playRangeSeconds)
+{
+  if (elapsed > playRangeSeconds.y )
+  {
+    elapsed = playRangeSeconds.x + fmod(elapsed, playRangeSeconds.y);
+  }
+  else if( elapsed < playRangeSeconds.x )
+  {
+    elapsed = playRangeSeconds.y - fmod(elapsed, playRangeSeconds.y);
+  }
+}
+
 }
 
 namespace Dali
@@ -40,20 +53,21 @@ namespace Internal
 namespace SceneGraph
 {
 
-Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, EndAction endAction, EndAction disconnectAction )
+Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction )
 {
-  return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, isLooping, endAction, disconnectAction );
+  return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, loopCount, endAction, disconnectAction );
 }
 
-Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
+Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
 : mDurationSeconds(durationSeconds),
   mSpeedFactor( speedFactor ),
-  mLooping(isLooping),
   mEndAction(endAction),
   mDisconnectAction(disconnectAction),
   mState(Stopped),
   mElapsedSeconds(playRange.x*mDurationSeconds),
-  mPlayCount(0),
+  mPlayedCount(0),
+  mLoopCount(loopCount),
+  mCurrentLoop(0),
   mPlayRange( playRange )
 {
 }
@@ -72,9 +86,10 @@ void Animation::SetDuration(float durationSeconds)
   mDurationSeconds = durationSeconds;
 }
 
-void Animation::SetLooping(bool looping)
+void Animation::SetLoopCount(int loopCount)
 {
-  mLooping = looping;
+  mLoopCount = loopCount;
+  mCurrentLoop = 0;
 }
 
 void Animation::SetEndAction(Dali::Animation::EndAction action)
@@ -113,6 +128,8 @@ void Animation::Play()
   }
 
   SetAnimatorsActive( true );
+
+  mCurrentLoop = 0;
 }
 
 void Animation::PlayFrom( float progress )
@@ -180,7 +197,8 @@ bool Animation::Stop(BufferIndex bufferIndex)
     }
 
     // The animation has now been played to completion
-    ++mPlayCount;
+    ++mPlayedCount;
+    mCurrentLoop = 0;
   }
 
   mElapsedSeconds = mPlayRange.x*mDurationSeconds;
@@ -216,12 +234,15 @@ void Animation::AddAnimator( AnimatorBase* animator )
   mAnimators.PushBack( animator );
 }
 
-bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
+void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished )
 {
+  looped = false;
+  finished = false;
+
   if (mState == Stopped || mState == Destroyed)
   {
     // Short circuit when animation isn't running
-    return false;
+    return;
   }
 
   // The animation must still be applied when Paused/Stopping
@@ -231,35 +252,60 @@ bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
   }
 
   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
-  if (mLooping)
+
+  if( 0 == mLoopCount )
   {
-    if (mElapsedSeconds > playRangeSeconds.y )
-    {
-      mElapsedSeconds = playRangeSeconds.x + fmod(mElapsedSeconds, playRangeSeconds.y);
-    }
-    else if( mElapsedSeconds < playRangeSeconds.x )
+    // loop forever
+    WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
+
+    UpdateAnimators(bufferIndex, false, false);
+
+    // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
+  }
+  else if( mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
+  {
+    // looping
+    looped =  (mState == Playing                                                 &&
+               (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
+                ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
+
+    WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
+
+    UpdateAnimators(bufferIndex, false, false);
+
+    if(looped)
     {
-      mElapsedSeconds = playRangeSeconds.y - fmod(mElapsedSeconds, playRangeSeconds.y);
+      ++mCurrentLoop;
+      // don't increment mPlayedCount until the finished final loop
     }
   }
+  else
+  {
+    // playing once (and last mCurrentLoop)
+    finished = (mState == Playing                                                 &&
+                (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
+                 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
 
-  const bool animationFinished(mState == Playing                                                &&
-                              (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
-                               ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x ))
-                              );
+    // update with bake if finished
+    UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished);
 
-  UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard), animationFinished);
+    if(finished)
+    {
+      // The animation has now been played to completion
+      ++mPlayedCount;
 
-  if (animationFinished)
-  {
-    // The animation has now been played to completion
-    ++mPlayCount;
+      // loop iterations come to this else branch for their final iterations
+      if( mCurrentLoop < mLoopCount)
+      {
+        ++mCurrentLoop;
+        DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
+      }
 
-    mElapsedSeconds = playRangeSeconds.x;
-    mState = Stopped;
+      mElapsedSeconds = playRangeSeconds.x;
+      mState = Stopped;
+    }
   }
 
-  return animationFinished;
 }
 
 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
index 94d42f0..1885560 100644 (file)
@@ -66,12 +66,12 @@ public:
    * @param[in] durationSeconds The duration of the animation in seconds.
    * @param[in] speedFactor Multiplier to the animation velocity.
    * @param[in] playRange Minimum and maximum progress between which the animation will play.
-   * @param[in] isLooping Whether the animation will loop.
+   * @param[in] loopCount The number of times the animation will loop. ( See SetLoopCount() )
    * @param[in] endAction The action to perform when the animation ends.
    * @param[in] disconnectAction The action to perform when the property owner of an animator is disconnected.
    * @return A new Animation
    */
-  static Animation* New( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, EndAction endAction, EndAction disconnectAction );
+  static Animation* New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction );
 
   /**
    * Virtual destructor
@@ -129,10 +129,11 @@ public:
   }
 
   /**
-   * Set whether the animation will loop.
-   * @param[in] looping True if the animation will loop.
+   * Set the animation loop count.
+   * 0 is loop forever, N loop play N times
+   * @param[in] loopCount The loop count
    */
-  void SetLooping(bool looping);
+  void SetLoopCount(int loopCount);
 
   /**
    * Query whether the animation will loop.
@@ -140,7 +141,16 @@ public:
    */
   bool IsLooping() const
   {
-    return mLooping;
+    return mLoopCount != 1;
+  }
+
+  /*
+   * Get the loop count
+   * @return the loop count
+   */
+  int GetLoopCount() const
+  {
+    return mLoopCount;
   }
 
   /**
@@ -226,9 +236,17 @@ public:
    * Retrive a count of the number of times the animation has been played to completion.
    * This can be used to emit "Finised" signals from the public-api
    */
-  int GetPlayCount() const
+  int GetPlayedCount() const
+  {
+    return mPlayedCount;
+  }
+
+  /**
+   * Get the current loop count from zero to GetLoopCount().
+   */
+  int GetCurrentLoop() const
   {
-    return mPlayCount;
+    return mCurrentLoop;
   }
 
   /**
@@ -254,9 +272,10 @@ public:
    * @pre The animation is playing or paused.
    * @param[in] bufferIndex The buffer to update.
    * @param[in] elapsedSeconds The time elapsed since the previous frame.
-   * @return True if the animation has finished.
+   * @param[out] looped True if the animation looped
+   * @param[out] finished True if the animation has finished.
    */
-  bool Update(BufferIndex bufferIndex, float elapsedSeconds);
+  void Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished );
 
 
 protected:
@@ -264,7 +283,7 @@ protected:
   /**
    * Protected constructor. See New()
    */
-  Animation( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, EndAction endAction, EndAction disconnectAction );
+  Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction );
 
 
 private:
@@ -301,13 +320,15 @@ protected:
 
   float mDurationSeconds;
   float mSpeedFactor;
-  bool mLooping;
   EndAction mEndAction;
   EndAction mDisconnectAction;
 
   State mState;
   float mElapsedSeconds;
-  int mPlayCount;
+  int mPlayedCount;              // Incremented at end of animation or completion of all loops
+                                 // Never incremented when looping forever. Event thread tracks to signal end.
+  int mLoopCount;                // N loop setting
+  int mCurrentLoop;              // Current loop number
 
   Vector2 mPlayRange;
   AnimatorContainer mAnimators;
@@ -334,15 +355,15 @@ inline void SetDurationMessage( EventThreadServices& eventThreadServices, const
   new (slot) LocalType( &animation, &Animation::SetDuration, durationSeconds );
 }
 
-inline void SetLoopingMessage( EventThreadServices& eventThreadServices, const Animation& animation, bool looping )
+inline void SetLoopingMessage( EventThreadServices& eventThreadServices, const Animation& animation, int loopCount )
 {
-  typedef MessageValue1< Animation, bool > LocalType;
+  typedef MessageValue1< Animation, int > LocalType;
 
   // Reserve some memory inside the message queue
   unsigned int* slot = eventThreadServices.ReserveMessageSlot( sizeof( LocalType ) );
 
   // Construct message in the message queue memory; note that delete should not be called on the return value
-  new (slot) LocalType( &animation, &Animation::SetLooping, looping );
+  new (slot) LocalType( &animation, &Animation::SetLoopCount, loopCount );
 }
 
 inline void SetEndActionMessage( EventThreadServices& eventThreadServices, const Animation& animation, Dali::Animation::EndAction action )
index 4c27d15..81107f7 100644 (file)
@@ -96,7 +96,7 @@ Layer* FindLayer( Node& node )
 /**
  * Rebuild the Layer::colorRenderables, stencilRenderables and overlayRenderables members,
  * including only renderers which are included in the current render-task.
- * Returns true if all renderers have finshed acquiring resources.
+ * Returns true if all renderers have finished acquiring resources.
  */
 bool AddRenderablesForTask( BufferIndex updateBufferIndex,
                             Node& node,
index 7a37ec4..8a36580 100644 (file)
@@ -715,12 +715,16 @@ void UpdateManager::Animate( BufferIndex bufferIndex, float elapsedSeconds )
 {
   AnimationContainer &animations = mImpl->animations;
   AnimationIter iter = animations.Begin();
+  bool animationLooped = false;
   while ( iter != animations.End() )
   {
     Animation* animation = *iter;
-    bool finished = animation->Update( bufferIndex, elapsedSeconds );
+    bool finished = false;
+    bool looped = false;
+    animation->Update( bufferIndex, elapsedSeconds, looped, finished );
 
     mImpl->animationFinishedDuringUpdate = mImpl->animationFinishedDuringUpdate || finished;
+    animationLooped = animationLooped || looped;
 
     // Remove animations that had been destroyed but were still waiting for an update
     if (animation->GetState() == Animation::Destroyed)
@@ -733,7 +737,8 @@ void UpdateManager::Animate( BufferIndex bufferIndex, float elapsedSeconds )
     }
   }
 
-  if ( mImpl->animationFinishedDuringUpdate )
+  // queue the notification on finished or looped (to update loop count)
+  if ( mImpl->animationFinishedDuringUpdate || animationLooped )
   {
     // The application should be notified by NotificationManager, in another thread
     mImpl->notificationManager.QueueCompleteNotification( &mImpl->animationFinishedNotifier );
index 32c8ebe..686d81b 100644 (file)
@@ -79,6 +79,21 @@ void Animation::SetLooping(bool looping)
   GetImplementation(*this).SetLooping(looping);
 }
 
+void Animation::SetLoopCount(int count)
+{
+  GetImplementation(*this).SetLoopCount(count);
+}
+
+int Animation::GetLoopCount()
+{
+  return GetImplementation(*this).GetLoopCount();
+}
+
+int Animation::GetCurrentLoop()
+{
+  return GetImplementation(*this).GetCurrentLoop();
+}
+
 bool Animation::IsLooping() const
 {
   return GetImplementation(*this).IsLooping();
index 61a5007..d562c63 100644 (file)
@@ -215,14 +215,50 @@ public:
   float GetDuration() const;
 
   /**
-   * @brief Set whether the animation will loop.
+   * @brief Set whether the animation will loop forever.
+   *
+   * This function resets the loop count and should not be used with SetLoopCount(int).
+   * Setting this parameter does not cause the animation to Play()
    *
    * @SINCE_1_0.0
-   * @param[in] looping True if the animation will loop.
+   * @param[in] looping If true then the animation will loop forever, if false it will never loop.
    */
   void SetLooping(bool looping);
 
   /**
+   * @brief Enable looping for 'count' repeats.
+   *
+   * A zero is the same as SetLooping(true) ie repeat forever.
+   * If Play() Stop() or 'count' loops is reached, the loop counter will reset.
+   * Setting this parameter does not cause the animation to Play()
+   *
+   * @SINCE_1_1.20
+   * @param[in] count The number of times to loop.
+   */
+  void SetLoopCount(int count);
+
+  /**
+   * @brief Get the loop count.
+   *
+   * A zero is the same as SetLooping(true) ie repeat forever.
+   * The loop count is initially 1 for play once.
+   *
+   * @SINCE_1_1.20
+   * @return The number of times to loop.
+   */
+  int GetLoopCount();
+
+  /**
+   * @brief Get the current loop count.
+   *
+   * A value 0 to GetLoopCount() indicating the current loop count when looping.
+   *
+   * @SINCE_1_1.20
+   * @return The current number of loops that have occured.
+   */
+  int GetCurrentLoop();
+
+  /**
    * @brief Query whether the animation will loop.
    *
    * @SINCE_1_0.0