2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/math/math-utils.h>
24 #include <dali/public-api/object/property-array.h>
27 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.h>
28 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h>
29 #include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
39 constexpr auto LOOP_FOREVER = -1;
40 constexpr auto MICROSECONDS_PER_SECOND(1e+6);
42 #if defined(DEBUG_ENABLED)
43 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
46 } // unnamed namespace
48 VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
49 : AsyncTask(MakeCallback(this, &VectorAnimationTask::TaskCompleted), AsyncTask::ThreadType::WORKER_THREAD),
51 mVectorRenderer(VectorAnimationRenderer::New()),
53 mVectorAnimationThread(factoryCache.GetVectorAnimationManager().GetVectorAnimationThread()),
55 mResourceReadySignal(),
56 mAnimationFinishedTrigger(),
57 mLoadCompletedTrigger(new EventThreadCallback(MakeCallback(this, &VectorAnimationTask::OnLoadCompleted))),
58 mPlayState(PlayState::STOPPED),
59 mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
60 mLoopingMode(DevelImageVisual::LoopingMode::RESTART),
61 mNextFrameStartTime(),
62 mFrameDurationMicroSeconds(MICROSECONDS_PER_SECOND / 60.0f),
71 mAnimationDataIndex(0),
72 mLoopCount(LOOP_FOREVER),
75 mUpdateFrameNumber(false),
76 mNeedAnimationFinishedTrigger(true),
77 mAnimationDataUpdated(false),
84 mVectorRenderer.UploadCompletedSignal().Connect(this, &VectorAnimationTask::OnUploadCompleted);
87 VectorAnimationTask::~VectorAnimationTask()
89 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::~VectorAnimationTask: destructor [%p]\n", this);
92 void VectorAnimationTask::Process()
94 mRasterized = Rasterize();
97 bool VectorAnimationTask::IsReady()
102 void VectorAnimationTask::Finalize()
104 ConditionalWait::ScopedLock lock(mConditionalWait);
106 // Release some objects in the main thread
107 if(mAnimationFinishedTrigger)
109 mAnimationFinishedTrigger.reset();
111 if(mLoadCompletedTrigger)
113 mLoadCompletedTrigger.reset();
116 mVectorRenderer.Finalize();
121 void VectorAnimationTask::TaskCompleted(VectorAnimationTaskPtr task)
123 mVectorAnimationThread.OnTaskCompleted(task, task->IsRasterized(), task->IsAnimating());
126 bool VectorAnimationTask::IsRasterized()
131 bool VectorAnimationTask::IsAnimating()
133 return mKeepAnimation;
136 bool VectorAnimationTask::Load(bool synchronousLoading)
138 if(!mVectorRenderer.Load(mUrl))
140 DALI_LOG_ERROR("VectorAnimationTask::Load: Load failed [%s]\n", mUrl.c_str());
141 mLoadRequest = false;
143 if(!synchronousLoading)
145 mLoadCompletedTrigger->Trigger();
150 mTotalFrame = mVectorRenderer.GetTotalFrameNumber();
152 mEndFrame = mTotalFrame - 1;
154 mFrameRate = mVectorRenderer.GetFrameRate();
155 mFrameDurationMicroSeconds = MICROSECONDS_PER_SECOND / mFrameRate;
157 mLoadRequest = false;
158 if(!synchronousLoading)
160 mLoadCompletedTrigger->Trigger();
163 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Load: file = %s [%d frames, %f fps] [%p]\n", mUrl.c_str(), mTotalFrame, mFrameRate, this);
168 void VectorAnimationTask::SetRenderer(Renderer renderer)
170 ConditionalWait::ScopedLock lock(mConditionalWait);
172 mVectorRenderer.SetRenderer(renderer);
174 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetRenderer [%p]\n", this);
177 void VectorAnimationTask::RequestLoad(const std::string& url, bool synchronousLoading)
181 if(!synchronousLoading)
185 mVectorAnimationThread.AddTask(this);
195 bool VectorAnimationTask::IsLoadRequested() const
200 void VectorAnimationTask::SetAnimationData(const AnimationData& data)
202 ConditionalWait::ScopedLock lock(mConditionalWait);
204 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetAnimationData [%p]\n", this);
206 uint32_t index = mAnimationDataIndex == 0 ? 1 : 0; // Use the other buffer
208 mAnimationData[index] = data;
209 mAnimationDataUpdated = true;
211 if(data.resendFlag & VectorAnimationTask::RESEND_SIZE)
213 // The size should be changed in the main thread.
214 SetSize(data.width, data.height);
217 mVectorAnimationThread.AddTask(this);
220 void VectorAnimationTask::SetSize(uint32_t width, uint32_t height)
222 if(mWidth != width || mHeight != height)
224 mVectorRenderer.SetSize(width, height);
229 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetSize: width = %d, height = %d [%p]\n", width, height, this);
233 void VectorAnimationTask::PlayAnimation()
235 if(mPlayState != PlayState::PLAYING)
237 mNeedAnimationFinishedTrigger = true;
238 mUpdateFrameNumber = false;
239 mPlayState = PlayState::PLAYING;
241 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::PlayAnimation: Play [%p]\n", this);
245 void VectorAnimationTask::StopAnimation()
247 if(mPlayState != PlayState::STOPPING)
249 mNeedAnimationFinishedTrigger = false;
250 mPlayState = PlayState::STOPPING;
252 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::StopAnimation: Stop [%p]\n", this);
256 void VectorAnimationTask::PauseAnimation()
258 if(mPlayState == PlayState::PLAYING)
260 mPlayState = PlayState::PAUSED;
262 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::PauseAnimation: Pause [%p]\n", this);
266 void VectorAnimationTask::SetAnimationFinishedCallback(EventThreadCallback* callback)
268 ConditionalWait::ScopedLock lock(mConditionalWait);
271 mAnimationFinishedTrigger = std::unique_ptr<EventThreadCallback>(callback);
275 void VectorAnimationTask::SetLoopCount(int32_t count)
277 if(mLoopCount != count)
282 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetLoopCount: [%d] [%p]\n", count, this);
286 void VectorAnimationTask::SetPlayRange(const Property::Array& playRange)
289 uint32_t startFrame = 0, endFrame = 0;
290 size_t count = playRange.Count();
294 int32_t start = 0, end = 0;
295 if(playRange.GetElementAt(0).Get(start) && playRange.GetElementAt(1).Get(end))
297 startFrame = static_cast<uint32_t>(start);
298 endFrame = static_cast<uint32_t>(end);
303 std::string startMarker, endMarker;
304 if(playRange.GetElementAt(0).Get(startMarker) && playRange.GetElementAt(1).Get(endMarker))
308 uint32_t frame; // We don't use this later
309 if(mVectorRenderer.GetMarkerInfo(startMarker, startFrame, frame) && mVectorRenderer.GetMarkerInfo(endMarker, frame, endFrame))
320 if(playRange.GetElementAt(0).Get(marker))
322 if(mVectorRenderer && mVectorRenderer.GetMarkerInfo(marker, startFrame, endFrame))
331 DALI_LOG_ERROR("VectorAnimationTask::SetPlayRange: Invalid range [%p]\n", this);
335 // Make sure the range specified is between 0 and the total frame number
336 startFrame = std::min(startFrame, mTotalFrame - 1);
337 endFrame = std::min(endFrame, mTotalFrame - 1);
339 // If the range is not in order swap values
340 if(startFrame > endFrame)
342 uint32_t temp = startFrame;
343 startFrame = endFrame;
347 if(startFrame != mStartFrame || endFrame != mEndFrame)
349 mStartFrame = startFrame;
350 mEndFrame = endFrame;
352 // If the current frame is out of the range, change the current frame also.
353 if(mStartFrame > mCurrentFrame)
355 mCurrentFrame = mStartFrame;
357 else if(mEndFrame < mCurrentFrame)
359 mCurrentFrame = mEndFrame;
362 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetPlayRange: [%d, %d] [%s] [%p]\n", mStartFrame, mEndFrame, mUrl.c_str(), this);
366 void VectorAnimationTask::GetPlayRange(uint32_t& startFrame, uint32_t& endFrame)
368 startFrame = mStartFrame;
369 endFrame = mEndFrame;
372 void VectorAnimationTask::SetCurrentFrameNumber(uint32_t frameNumber)
374 if(mCurrentFrame == frameNumber)
376 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetCurrentFrameNumber: Set same frame [%d] [%p]\n", frameNumber, this);
380 if(frameNumber >= mStartFrame && frameNumber <= mEndFrame)
382 mCurrentFrame = frameNumber;
383 mUpdateFrameNumber = false;
385 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetCurrentFrameNumber: frame number = %d [%p]\n", mCurrentFrame, this);
389 DALI_LOG_ERROR("Invalid frame number [%d (%d, %d)] [%p]\n", frameNumber, mStartFrame, mEndFrame, this);
393 uint32_t VectorAnimationTask::GetCurrentFrameNumber() const
395 return mCurrentFrame;
398 uint32_t VectorAnimationTask::GetTotalFrameNumber() const
403 void VectorAnimationTask::GetDefaultSize(uint32_t& width, uint32_t& height) const
405 mVectorRenderer.GetDefaultSize(width, height);
408 void VectorAnimationTask::SetStopBehavior(DevelImageVisual::StopBehavior::Type stopBehavior)
410 mStopBehavior = stopBehavior;
412 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetStopBehavior: stop behavor = %d [%p]\n", mStopBehavior, this);
415 void VectorAnimationTask::SetLoopingMode(DevelImageVisual::LoopingMode::Type loopingMode)
417 mLoopingMode = loopingMode;
419 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetLoopingMode: looping mode = %d [%p]\n", mLoopingMode, this);
422 void VectorAnimationTask::GetLayerInfo(Property::Map& map) const
424 mVectorRenderer.GetLayerInfo(map);
427 VectorAnimationTask::ResourceReadySignalType& VectorAnimationTask::ResourceReadySignal()
429 return mResourceReadySignal;
432 bool VectorAnimationTask::Rasterize()
434 bool stopped = false;
435 uint32_t currentFrame;
436 mKeepAnimation = false;
439 ConditionalWait::ScopedLock lock(mConditionalWait);
442 // The task will be destroyed. We don't need rasterization.
457 ApplyAnimationData();
459 if(mPlayState == PlayState::PLAYING && mUpdateFrameNumber)
461 mCurrentFrame = mForward ? mCurrentFrame + mDroppedFrames + 1 : (mCurrentFrame > mDroppedFrames ? mCurrentFrame - mDroppedFrames - 1 : 0);
462 Dali::ClampInPlace(mCurrentFrame, mStartFrame, mEndFrame);
465 currentFrame = mCurrentFrame;
467 mUpdateFrameNumber = true;
469 if(mPlayState == PlayState::STOPPING)
471 mCurrentFrame = GetStoppedFrame(mStartFrame, mEndFrame, mCurrentFrame);
472 currentFrame = mCurrentFrame;
475 else if(mPlayState == PlayState::PLAYING)
477 bool animationFinished = false;
479 if(currentFrame >= mEndFrame) // last frame
481 if(mLoopingMode == DevelImageVisual::LoopingMode::AUTO_REVERSE)
487 if(mLoopCount < 0 || ++mCurrentLoop < mLoopCount) // repeat forever or before the last loop
489 mCurrentFrame = mStartFrame;
490 mUpdateFrameNumber = false;
494 animationFinished = true; // end of animation
498 else if(currentFrame == mStartFrame && !mForward) // first frame
500 if(mLoopCount < 0 || ++mCurrentLoop < mLoopCount) // repeat forever or before the last loop
506 animationFinished = true; // end of animation
510 if(animationFinished)
512 if(mStopBehavior == DevelImageVisual::StopBehavior::CURRENT_FRAME)
518 mPlayState = PlayState::STOPPING;
524 bool renderSuccess = false;
527 renderSuccess = mVectorRenderer.Render(currentFrame);
530 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Rasterize: Rendering failed. Try again later.[%d] [%p]\n", currentFrame, this);
531 mUpdateFrameNumber = false;
535 if(stopped && renderSuccess)
537 mPlayState = PlayState::STOPPED;
543 // Notify the Renderer that rendering is stopped.
544 mVectorRenderer.RenderStopped();
547 // Animation is finished
549 ConditionalWait::ScopedLock lock(mConditionalWait);
550 if(mNeedAnimationFinishedTrigger && mAnimationFinishedTrigger)
552 mAnimationFinishedTrigger->Trigger();
556 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Rasterize: Animation is finished [current = %d] [%p]\n", currentFrame, this);
559 if(mPlayState != PlayState::PAUSED && mPlayState != PlayState::STOPPED)
561 mKeepAnimation = true;
567 uint32_t VectorAnimationTask::GetStoppedFrame(uint32_t startFrame, uint32_t endFrame, uint32_t currentFrame)
569 uint32_t frame = currentFrame;
571 switch(mStopBehavior)
573 case DevelImageVisual::StopBehavior::FIRST_FRAME:
578 case DevelImageVisual::StopBehavior::LAST_FRAME:
580 if(mLoopingMode == DevelImageVisual::LoopingMode::AUTO_REVERSE)
590 case DevelImageVisual::StopBehavior::CURRENT_FRAME:
592 frame = currentFrame;
600 VectorAnimationTask::TimePoint VectorAnimationTask::CalculateNextFrameTime(bool renderNow)
602 // std::chrono::time_point template has second parameter duration which defaults to the std::chrono::steady_clock supported
603 // duration. In some C++11 implementations it is a milliseconds duration, so it fails to compile unless mNextFrameStartTime
604 // is casted to use the default duration.
605 mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds));
606 auto current = std::chrono::steady_clock::now();
611 mNextFrameStartTime = current;
613 else if(mNextFrameStartTime < current)
615 uint32_t droppedFrames = 0;
617 while(current > std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds)) && droppedFrames < mTotalFrame)
620 mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds));
623 mNextFrameStartTime = current;
624 mDroppedFrames = droppedFrames;
627 return mNextFrameStartTime;
630 VectorAnimationTask::TimePoint VectorAnimationTask::GetNextFrameTime()
632 return mNextFrameStartTime;
635 void VectorAnimationTask::ApplyAnimationData()
640 ConditionalWait::ScopedLock lock(mConditionalWait);
642 if(!mAnimationDataUpdated || mAnimationData[mAnimationDataIndex].resendFlag != 0)
644 // Data is not updated or the previous data is not applied yet.
648 mAnimationDataIndex = mAnimationDataIndex == 0 ? 1 : 0; // Swap index
649 mAnimationDataUpdated = false;
651 index = mAnimationDataIndex;
654 if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_LOOP_COUNT)
656 SetLoopCount(mAnimationData[index].loopCount);
659 if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_PLAY_RANGE)
661 SetPlayRange(mAnimationData[index].playRange);
664 if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_STOP_BEHAVIOR)
666 SetStopBehavior(mAnimationData[index].stopBehavior);
669 if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_LOOPING_MODE)
671 SetLoopingMode(mAnimationData[index].loopingMode);
674 if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_CURRENT_FRAME)
676 SetCurrentFrameNumber(mAnimationData[index].currentFrame);
679 if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_NEED_RESOURCE_READY)
681 mVectorRenderer.InvalidateBuffer();
684 if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_DYNAMIC_PROPERTY)
686 for(auto&& iter : mAnimationData[index].dynamicProperties)
688 mVectorRenderer.AddPropertyValueCallback(iter.keyPath, static_cast<VectorAnimationRenderer::VectorProperty>(iter.property), iter.callback, iter.id);
692 if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
694 if(mAnimationData[index].playState == DevelImageVisual::PlayState::PLAYING)
698 else if(mAnimationData[index].playState == DevelImageVisual::PlayState::PAUSED)
702 else if(mAnimationData[index].playState == DevelImageVisual::PlayState::STOPPED)
709 mAnimationData[index].dynamicProperties.clear();
710 mAnimationData[index].resendFlag = 0;
713 void VectorAnimationTask::OnUploadCompleted()
715 mResourceReadySignal.Emit(ResourceStatus::READY);
718 void VectorAnimationTask::OnLoadCompleted()
722 mResourceReadySignal.Emit(ResourceStatus::LOADED);
726 mResourceReadySignal.Emit(ResourceStatus::FAILED);
729 } // namespace Internal
731 } // namespace Toolkit