/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
Property::Map propertyMap;
propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
- .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
- .Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+ .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME);
Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
DALI_TEST_CHECK(visual);
application.GetScene().Add(actor);
+ application.SendNotification();
+ application.Render();
+
+ // Trigger count is 1 - render a frame
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ propertyMap.Clear();
+ propertyMap.Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+ DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, propertyMap);
+
Property::Map attributes;
DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
application.SendNotification();
application.Render();
- // Wait for animation finish - render, finish
- DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+ // Wait for animation finish
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
Property::Map map = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
Property::Value* value = map.Find(DevelImageVisual::Property::PLAY_STATE);
Property::Map propertyMap;
propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
.Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
- .Add(DevelImageVisual::Property::LOOP_COUNT, 3)
- .Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::FIRST_FRAME)
.Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
application.GetScene().Add(actor);
+ application.SendNotification();
+ application.Render();
+
+ // Trigger count is 2 - load & render a frame
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+ propertyMap.Clear();
+ propertyMap.Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+ propertyMap.Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::FIRST_FRAME);
+ DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, propertyMap);
+
Property::Map attributes;
DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
application.SendNotification();
application.Render();
- // Trigger count is 3 - load, render, animation finished
- DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+ // Trigger count is 1 - animation finished
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
Property::Map map = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
Property::Value* value = map.Find(DevelImageVisual::Property::CURRENT_FRAME_NUMBER);
Property::Map propertyMap;
propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
.Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
- .Add(DevelImageVisual::Property::LOOP_COUNT, 3)
- .Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::LAST_FRAME)
- .Add(DevelImageVisual::Property::LOOPING_MODE, DevelImageVisual::LoopingMode::AUTO_REVERSE)
.Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
application.GetScene().Add(actor);
+ application.SendNotification();
+ application.Render();
+
+ // Trigger count is 2 - load, render
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+ propertyMap.Clear();
+ propertyMap.Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+ propertyMap.Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::LAST_FRAME);
+ propertyMap.Add(DevelImageVisual::Property::LOOPING_MODE, DevelImageVisual::LoopingMode::AUTO_REVERSE);
+ DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, propertyMap);
+
Property::Map attributes;
DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
application.SendNotification();
application.Render();
- // Trigger count is 3 - load, render, animation finished
- DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+ // Trigger count is 1 - animation finished
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
Property::Map map = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
Property::Value* value = map.Find(DevelImageVisual::Property::CURRENT_FRAME_NUMBER);
application.GetScene().Add(actor1);
+ application.SendNotification();
+ application.Render();
+
+ // Trigger count is 2 - load & render a frame
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
propertyMap.Clear();
propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
.Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
application.SendNotification();
application.Render();
- // Trigger count is 4 - load & render a frame for each instance
- DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(4), true, TEST_LOCATION);
+ // Trigger count is 2 - load & render a frame
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
DevelControl::DoAction(actor2, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, Property::Map());
mVectorAnimationThread(factoryCache.GetVectorAnimationManager().GetVectorAnimationThread()),
mConditionalWait(),
mResourceReadySignal(),
- mAnimationFinishedTrigger(),
- mLoadCompletedTrigger(new EventThreadCallback(MakeCallback(this, &VectorAnimationTask::OnLoadCompleted))),
+ mLoadCompletedCallback(MakeCallback(this, &VectorAnimationTask::OnLoadCompleted)),
mPlayState(PlayState::STOPPED),
mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
mLoopingMode(DevelImageVisual::LoopingMode::RESTART),
ConditionalWait::ScopedLock lock(mConditionalWait);
// Release some objects in the main thread
- if(mAnimationFinishedTrigger)
+ if(mAnimationFinishedCallback)
{
- mAnimationFinishedTrigger.reset();
+ mVectorAnimationThread.RemoveEventTriggerCallback(mAnimationFinishedCallback.get());
}
- if(mLoadCompletedTrigger)
+ if(mLoadCompletedCallback)
{
- mLoadCompletedTrigger.reset();
+ mVectorAnimationThread.RemoveEventTriggerCallback(mLoadCompletedCallback.get());
}
mVectorRenderer.Finalize();
mLoadFailed = true;
if(!synchronousLoading)
{
- mLoadCompletedTrigger->Trigger();
+ mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
}
return false;
}
mLoadRequest = false;
if(!synchronousLoading)
{
- mLoadCompletedTrigger->Trigger();
+ mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
}
DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Load: file = %s [%d frames, %f fps] [%p]\n", mUrl.c_str(), mTotalFrame, mFrameRate, this);
}
}
-void VectorAnimationTask::SetAnimationFinishedCallback(EventThreadCallback* callback)
+void VectorAnimationTask::SetAnimationFinishedCallback(CallbackBase* callback)
{
ConditionalWait::ScopedLock lock(mConditionalWait);
- if(callback)
- {
- mAnimationFinishedTrigger = std::unique_ptr<EventThreadCallback>(callback);
- }
+ mAnimationFinishedCallback = std::unique_ptr<CallbackBase>(callback);
}
void VectorAnimationTask::SetLoopCount(int32_t count)
// Animation is finished
{
ConditionalWait::ScopedLock lock(mConditionalWait);
- if(mNeedAnimationFinishedTrigger && mAnimationFinishedTrigger)
+ if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback)
{
- mAnimationFinishedTrigger->Trigger();
+ mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get());
}
}
{
mAsyncTaskManager = Dali::AsyncTaskManager::Get();
mSleepThread.Start();
+
+ mEventTrigger = std::unique_ptr<EventThreadCallback>(new EventThreadCallback(MakeCallback(this, &VectorAnimationThread::OnEventCallbackTriggered)));
}
VectorAnimationThread::~VectorAnimationThread()
}
}
+void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback)
+{
+ ConditionalWait::ScopedLock lock(mConditionalWait);
+ mTriggerEventCallbacks.push_back(callback);
+
+ if(!mEventTriggered)
+ {
+ mEventTrigger->Trigger();
+ mEventTriggered = true;
+ }
+}
+
+void VectorAnimationThread::RemoveEventTriggerCallback(CallbackBase* callback)
+{
+ ConditionalWait::ScopedLock lock(mConditionalWait);
+ auto iter = std::find(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), callback);
+ if(iter != mTriggerEventCallbacks.end())
+ {
+ mTriggerEventCallbacks.erase(iter);
+ }
+}
+
void VectorAnimationThread::Run()
{
SetThreadName("VectorAnimationThread");
}
}
+void VectorAnimationThread::OnEventCallbackTriggered()
+{
+ ConditionalWait::ScopedLock lock(mConditionalWait);
+
+ for(auto&& iter : mTriggerEventCallbacks)
+ {
+ CallbackBase::Execute(*iter);
+ }
+
+ mTriggerEventCallbacks.clear();
+ mEventTriggered = false;
+}
+
VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
: mConditionalWait(),
mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
~VectorAnimationThread() override;
/**
- * Add a animation task into the vector animation thread, called by main thread.
+ * @brief Add a animation task into the vector animation thread, called by main thread.
*
* @param[in] task The task added to the thread.
*/
/**
* @brief Called when the rasterization is completed from the rasterize thread.
+ *
* @param[in] task The completed task
* @param[in] success true if the task succeeded, false otherwise.
* @param[in] keepAnimation true if the animation is running, false otherwise.
*/
void OnAwakeFromSleep();
+ /**
+ * @brief Add an event trigger callback.
+ *
+ * @param callback The callback to add
+ * @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);
+
+ /**
+ * @brief Remove an event trigger callback.
+ *
+ * @param callback The callback to remove
+ */
+ void RemoveEventTriggerCallback(CallbackBase* callback);
+
protected:
/**
* @brief The entry function of the animation thread.
void Rasterize();
/**
+ * @brief Called when the event callback is triggered.
+ */
+ void OnEventCallbackTriggered();
+
+ /**
* @brief The thread to sleep until the next frame time.
*/
class SleepThread : public Thread
VectorAnimationThread& operator=(const VectorAnimationThread& thread) = delete;
private:
- std::vector<VectorAnimationTaskPtr> mAnimationTasks;
- std::vector<VectorAnimationTaskPtr> mCompletedTasks;
- std::vector<VectorAnimationTaskPtr> mWorkingTasks;
- SleepThread mSleepThread;
- ConditionalWait mConditionalWait;
- bool mNeedToSleep;
- bool mDestroyThread;
- const Dali::LogFactoryInterface& mLogFactory;
- Dali::AsyncTaskManager mAsyncTaskManager;
+ std::vector<VectorAnimationTaskPtr> mAnimationTasks;
+ std::vector<VectorAnimationTaskPtr> mCompletedTasks;
+ std::vector<VectorAnimationTaskPtr> mWorkingTasks;
+ std::vector<CallbackBase*> mTriggerEventCallbacks{}; // Callbacks are not owned
+ SleepThread mSleepThread;
+ ConditionalWait mConditionalWait;
+ std::unique_ptr<EventThreadCallback> mEventTrigger{};
+ bool mNeedToSleep;
+ bool mDestroyThread;
+ bool mEventTriggered{false};
+ const Dali::LogFactoryInterface& mLogFactory;
+ Dali::AsyncTaskManager mAsyncTaskManager;
};
} // namespace Internal