Merge "Fix bug in transition effect" into devel/master
authorsunghyun kim <scholb.kim@samsung.com>
Mon, 8 Apr 2024 06:10:10 +0000 (06:10 +0000)
committerGerrit Code Review <gerrit@review>
Mon, 8 Apr 2024 06:10:10 +0000 (06:10 +0000)
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h

index f078dc8..9c81c3b 100644 (file)
@@ -97,6 +97,7 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual(VisualFactoryCache& factory
   mPlacementActor(),
   mPlayState(DevelImageVisual::PlayState::STOPPED),
   mEventCallback(nullptr),
+  mLastSentPlayStateId(0u),
   mLoadFailed(false),
   mRendererAdded(false),
   mCoreShutdown(false),
@@ -565,11 +566,9 @@ void AnimatedVectorImageVisual::OnDoAction(const Property::Index actionId, const
     {
       if(IsOnScene() && mVisualSize != Vector2::ZERO)
       {
-        if(mAnimationData.playState != DevelImageVisual::PlayState::PLAYING)
-        {
-          mAnimationData.playState = DevelImageVisual::PlayState::PLAYING;
-          mAnimationData.resendFlag |= VectorAnimationTask::RESEND_PLAY_STATE;
-        }
+        // Always resend Playing state. If task is already playing, it will be ignored at Rasterize time.
+        mAnimationData.playState = DevelImageVisual::PlayState::PLAYING;
+        mAnimationData.resendFlag |= VectorAnimationTask::RESEND_PLAY_STATE;
       }
       mPlayState = DevelImageVisual::PlayState::PLAYING;
       break;
@@ -695,8 +694,14 @@ void AnimatedVectorImageVisual::OnResourceReady(VectorAnimationTask::ResourceSta
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "status = %d [%p]\n", status, this);
 }
 
-void AnimatedVectorImageVisual::OnAnimationFinished()
+void AnimatedVectorImageVisual::OnAnimationFinished(uint32_t playStateId)
 {
+  // Only send event when animation is finished by the last Play/Pause/Stop request.
+  if(mLastSentPlayStateId != playStateId)
+  {
+    return;
+  }
+
   AnimatedVectorImageVisualPtr self = this; // Keep reference until this API finished
 
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "AnimatedVectorImageVisual::OnAnimationFinished: action state = %d [%p]\n", mPlayState, this);
@@ -723,6 +728,13 @@ void AnimatedVectorImageVisual::SendAnimationData()
 {
   if(mAnimationData.resendFlag)
   {
+    if(mAnimationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
+    {
+      // Keep last sent playId. It will be used when we try to emit AnimationFinished signal.
+      // The OnAnimationFinished signal what before Play/Pause/Stop action send could be come after action sent.
+      // To ensure the OnAnimationFinished signal comes belong to what we sent, we need to keep last sent playId.
+      mAnimationData.playStateId = ++mLastSentPlayStateId;
+    }
     mVectorAnimationTask->SetAnimationData(mAnimationData);
 
     if(mImpl->mRenderer)
index 493e621..fc7d7a4 100644 (file)
@@ -182,8 +182,10 @@ private:
 
   /**
    * @brief Event callback from rasterize thread. This is called after the animation is finished.
+   *
+   * @param[in] playStateId The play state id
    */
-  void OnAnimationFinished();
+  void OnAnimationFinished(uint32_t playStateId);
 
   /**
    * @brief Send animation data to the rasterize thread.
@@ -250,6 +252,8 @@ private:
   DevelImageVisual::PlayState::Type  mPlayState;
   CallbackBase*                      mEventCallback; // Not owned
 
+  uint32_t mLastSentPlayStateId;
+
   bool mLoadFailed : 1;
   bool mRendererAdded : 1;
   bool mCoreShutdown : 1;
index cb67bc7..4f4e97d 100644 (file)
@@ -92,6 +92,7 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
   mWidth(0),
   mHeight(0),
   mAnimationDataIndex(0),
+  mAppliedPlayStateId(0u),
   mLoopCount(LOOP_FOREVER),
   mCurrentLoop(0),
   mForward(true),
@@ -214,7 +215,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
       Mutex::ScopedLock lock(mMutex);
       if(!synchronousLoading && mLoadCompletedCallback)
       {
-        mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+        mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
       }
     }
 #ifdef TRACE_ENABLED
@@ -245,7 +246,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
     Mutex::ScopedLock lock(mMutex);
     if(!synchronousLoading && mLoadCompletedCallback)
     {
-      mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+      mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
     }
   }
 
@@ -290,7 +291,7 @@ void VectorAnimationTask::RequestLoad(const VisualUrl& url, EncodedImageBuffer e
   {
     Load(true);
 
-    OnLoadCompleted();
+    OnLoadCompleted(0u);
   }
 }
 
@@ -703,7 +704,7 @@ bool VectorAnimationTask::Rasterize()
       Mutex::ScopedLock lock(mMutex);
       if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback)
       {
-        mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get());
+        mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get(), mAppliedPlayStateId);
       }
     }
 
@@ -859,6 +860,7 @@ void VectorAnimationTask::ApplyAnimationData()
 
     if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
     {
+      mAppliedPlayStateId = animationData.playStateId;
       if(animationData.playState == DevelImageVisual::PlayState::PLAYING)
       {
         PlayAnimation();
@@ -883,7 +885,7 @@ void VectorAnimationTask::OnUploadCompleted()
   mResourceReadySignal.Emit(ResourceStatus::READY);
 }
 
-void VectorAnimationTask::OnLoadCompleted()
+void VectorAnimationTask::OnLoadCompleted(uint32_t /* not used */)
 {
   if(!mLoadFailed)
   {
index ef89500..eb48a13 100644 (file)
@@ -93,7 +93,8 @@ public:
       currentFrame(0),
       width(0),
       height(0),
-      loopCount(-1)
+      loopCount(-1),
+      playStateId(0)
     {
     }
 
@@ -108,6 +109,7 @@ public:
       width        = rhs.width;
       height       = rhs.height;
       loopCount    = rhs.loopCount;
+      playStateId  = rhs.playStateId;
       dynamicProperties.insert(dynamicProperties.end(), rhs.dynamicProperties.begin(), rhs.dynamicProperties.end());
       return *this;
     }
@@ -122,6 +124,7 @@ public:
     uint32_t                             width;
     uint32_t                             height;
     int32_t                              loopCount;
+    uint32_t                             playStateId;
   };
 
   /**
@@ -365,7 +368,7 @@ private:
   /**
    * @brief Event callback from rasterize thread. This is called when the file loading is completed.
    */
-  void OnLoadCompleted();
+  void OnLoadCompleted(uint32_t argument);
 
   // Undefined
   VectorAnimationTask(const VectorAnimationTask& task) = delete;
@@ -407,6 +410,7 @@ private:
   uint32_t                             mWidth;
   uint32_t                             mHeight;
   uint32_t                             mAnimationDataIndex;
+  uint32_t                             mAppliedPlayStateId;
   int32_t                              mLoopCount;
   int32_t                              mCurrentLoop;
   bool                                 mForward : 1;
index 3ee2d5d..b07ba5b 100644 (file)
@@ -159,12 +159,12 @@ void VectorAnimationThread::OnAwakeFromSleep()
   }
 }
 
-void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback)
+void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback, uint32_t argument)
 {
   Mutex::ScopedLock lock(mEventTriggerMutex);
   if(!mDestroyThread)
   {
-    mTriggerEventCallbacks.push_back(callback);
+    mTriggerEventCallbacks.emplace_back(callback, argument);
 
     if(!mEventTriggered)
     {
@@ -179,7 +179,7 @@ void VectorAnimationThread::RemoveEventTriggerCallbacks(CallbackBase* callback)
   Mutex::ScopedLock lock(mEventTriggerMutex);
   if(!mDestroyThread)
   {
-    auto iter = std::remove(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), callback);
+    auto iter = std::remove_if(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), [&callback](std::pair<CallbackBase*, uint32_t>& item) { return item.first == callback; });
     mTriggerEventCallbacks.erase(iter, mTriggerEventCallbacks.end());
   }
 }
@@ -276,27 +276,32 @@ void VectorAnimationThread::Rasterize()
 
 void VectorAnimationThread::OnEventCallbackTriggered()
 {
-  while(CallbackBase* callback = GetNextEventCallback())
+  while(true)
   {
-    CallbackBase::Execute(*callback);
+    auto callbackPair = GetNextEventCallback();
+    if(callbackPair.first == nullptr)
+    {
+      break;
+    }
+    CallbackBase::Execute(*callbackPair.first, callbackPair.second);
   }
 }
 
-CallbackBase* VectorAnimationThread::GetNextEventCallback()
+std::pair<CallbackBase*, uint32_t> VectorAnimationThread::GetNextEventCallback()
 {
   Mutex::ScopedLock lock(mEventTriggerMutex);
   if(!mDestroyThread)
   {
     if(!mTriggerEventCallbacks.empty())
     {
-      auto          iter     = mTriggerEventCallbacks.begin();
-      CallbackBase* callback = *iter;
+      auto iter           = mTriggerEventCallbacks.begin();
+      auto callbackIdPair = *iter;
       mTriggerEventCallbacks.erase(iter);
-      return callback;
+      return callbackIdPair;
     }
     mEventTriggered = false;
   }
-  return nullptr;
+  return std::make_pair(nullptr, 0u);
 }
 
 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
index 7421cc8..b1f608a 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_VECTOR_ANIMATION_THREAD_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -77,10 +77,11 @@ public:
    * @brief Add an event trigger callback.
    *
    * @param callback The callback to add
+   * @param argument The argument to pass to the callback
    * @note Ownership of the callback is NOT passed onto this class.
    * @note The callback will be excuted in the main thread.
    */
-  void AddEventTriggerCallback(CallbackBase* callback);
+  void AddEventTriggerCallback(CallbackBase* callback, uint32_t argument);
 
   /**
    * @brief Remove event trigger callbacks what we added before.
@@ -109,7 +110,7 @@ private:
   /**
    * @brief Gets next event callback to process.
    */
-  CallbackBase* GetNextEventCallback();
+  std::pair<CallbackBase*, uint32_t> GetNextEventCallback();
 
   /**
    * @brief The thread to sleep until the next frame time.
@@ -160,20 +161,20 @@ private:
   VectorAnimationThread& operator=(const VectorAnimationThread& thread) = delete;
 
 private:
-  std::vector<VectorAnimationTaskPtr>  mAnimationTasks;
-  std::vector<VectorAnimationTaskPtr>  mCompletedTasks;
-  std::vector<VectorAnimationTaskPtr>  mWorkingTasks;
-  std::vector<CallbackBase*>           mTriggerEventCallbacks{}; // Callbacks are not owned
-  SleepThread                          mSleepThread;
-  ConditionalWait                      mConditionalWait;
-  Mutex                                mEventTriggerMutex;
-  std::unique_ptr<EventThreadCallback> mEventTrigger{};
-  bool                                 mNeedToSleep;
-  bool                                 mDestroyThread;
-  bool                                 mEventTriggered{false};
-  const Dali::LogFactoryInterface&     mLogFactory;
-  const Dali::TraceFactoryInterface&   mTraceFactory;
-  Dali::AsyncTaskManager               mAsyncTaskManager;
+  std::vector<VectorAnimationTaskPtr>             mAnimationTasks;
+  std::vector<VectorAnimationTaskPtr>             mCompletedTasks;
+  std::vector<VectorAnimationTaskPtr>             mWorkingTasks;
+  std::vector<std::pair<CallbackBase*, uint32_t>> mTriggerEventCallbacks{}; // Callbacks are not owned
+  SleepThread                                     mSleepThread;
+  ConditionalWait                                 mConditionalWait;
+  Mutex                                           mEventTriggerMutex;
+  std::unique_ptr<EventThreadCallback>            mEventTrigger{};
+  bool                                            mNeedToSleep;
+  bool                                            mDestroyThread;
+  bool                                            mEventTriggered{false};
+  const Dali::LogFactoryInterface&                mLogFactory;
+  const Dali::TraceFactoryInterface&              mTraceFactory;
+  Dali::AsyncTaskManager                          mAsyncTaskManager;
 };
 
 } // namespace Internal