mPlacementActor(),
mPlayState(DevelImageVisual::PlayState::STOPPED),
mEventCallback(nullptr),
+ mLastSentPlayStateId(0u),
mLoadFailed(false),
mRendererAdded(false),
mCoreShutdown(false),
{
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;
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);
{
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)
/**
* @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.
DevelImageVisual::PlayState::Type mPlayState;
CallbackBase* mEventCallback; // Not owned
+ uint32_t mLastSentPlayStateId;
+
bool mLoadFailed : 1;
bool mRendererAdded : 1;
bool mCoreShutdown : 1;
mWidth(0),
mHeight(0),
mAnimationDataIndex(0),
+ mAppliedPlayStateId(0u),
mLoopCount(LOOP_FOREVER),
mCurrentLoop(0),
mForward(true),
Mutex::ScopedLock lock(mMutex);
if(!synchronousLoading && mLoadCompletedCallback)
{
- mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+ mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
}
}
#ifdef TRACE_ENABLED
Mutex::ScopedLock lock(mMutex);
if(!synchronousLoading && mLoadCompletedCallback)
{
- mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
+ mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
}
}
{
Load(true);
- OnLoadCompleted();
+ OnLoadCompleted(0u);
}
}
Mutex::ScopedLock lock(mMutex);
if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback)
{
- mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get());
+ mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get(), mAppliedPlayStateId);
}
}
if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
{
+ mAppliedPlayStateId = animationData.playStateId;
if(animationData.playState == DevelImageVisual::PlayState::PLAYING)
{
PlayAnimation();
mResourceReadySignal.Emit(ResourceStatus::READY);
}
-void VectorAnimationTask::OnLoadCompleted()
+void VectorAnimationTask::OnLoadCompleted(uint32_t /* not used */)
{
if(!mLoadFailed)
{
currentFrame(0),
width(0),
height(0),
- loopCount(-1)
+ loopCount(-1),
+ playStateId(0)
{
}
width = rhs.width;
height = rhs.height;
loopCount = rhs.loopCount;
+ playStateId = rhs.playStateId;
dynamicProperties.insert(dynamicProperties.end(), rhs.dynamicProperties.begin(), rhs.dynamicProperties.end());
return *this;
}
uint32_t width;
uint32_t height;
int32_t loopCount;
+ uint32_t playStateId;
};
/**
/**
* @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;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mAnimationDataIndex;
+ uint32_t mAppliedPlayStateId;
int32_t mLoopCount;
int32_t mCurrentLoop;
bool mForward : 1;
}
}
-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)
{
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());
}
}
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)
#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.
* @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.
/**
* @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.
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