(Vector) Add NOTIFY_AFTER_RASTERIZATION property for low fps file + Use UpdateOnce...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-vector-image / vector-animation-task.cpp
1 /*
2  * Copyright (c) 2024 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/file-loader.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/trace.h>
25 #include <dali/public-api/math/math-utils.h>
26 #include <dali/public-api/object/property-array.h>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.h>
30 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h>
31 #include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
32
33 #ifdef TRACE_ENABLED
34 #include <chrono>
35 #include <iomanip>
36 #include <sstream>
37 #include <thread>
38 #endif
39
40 namespace Dali
41 {
42 namespace Toolkit
43 {
44 namespace Internal
45 {
46 namespace
47 {
48 constexpr auto LOOP_FOREVER = -1;
49 constexpr auto MICROSECONDS_PER_SECOND(1e+6);
50
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
53 #endif
54
55 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_IMAGE_PERFORMANCE_MARKER, false);
56
57 #ifdef TRACE_ENABLED
58 uint64_t GetNanoseconds()
59 {
60   // Get the time of a monotonic clock since its epoch.
61   auto epoch    = std::chrono::steady_clock::now().time_since_epoch();
62   auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(epoch);
63   return static_cast<uint64_t>(duration.count());
64 }
65 #endif
66
67 } // unnamed namespace
68
69 VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
70 : AsyncTask(MakeCallback(this, &VectorAnimationTask::TaskCompleted), AsyncTask::PriorityType::HIGH, AsyncTask::ThreadType::WORKER_THREAD),
71   mImageUrl(),
72   mEncodedImageBuffer(),
73   mVectorRenderer(VectorAnimationRenderer::New()),
74   mAnimationData(),
75   mVectorAnimationThread(factoryCache.GetVectorAnimationManager().GetVectorAnimationThread()),
76   mMutex(),
77   mResourceReadySignal(),
78   mLoadCompletedCallback(MakeCallback(this, &VectorAnimationTask::OnLoadCompleted)),
79   mCachedLayerInfo(),
80   mCachedMarkerInfo(),
81   mPlayState(PlayState::STOPPED),
82   mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
83   mLoopingMode(DevelImageVisual::LoopingMode::RESTART),
84   mNextFrameStartTime(),
85   mFrameDurationMicroSeconds(MICROSECONDS_PER_SECOND / 60.0f),
86   mFrameRate(60.0f),
87   mCurrentFrame(0),
88   mTotalFrame(0),
89   mStartFrame(0),
90   mEndFrame(0),
91   mDroppedFrames(0),
92   mWidth(0),
93   mHeight(0),
94   mAnimationDataIndex(0),
95   mAppliedPlayStateId(0u),
96   mLoopCount(LOOP_FOREVER),
97   mCurrentLoop(0),
98   mForward(true),
99   mUpdateFrameNumber(false),
100   mNeedAnimationFinishedTrigger(true),
101   mNeedForceRenderOnceTrigger(false),
102   mAnimationDataUpdated(false),
103   mDestroyTask(false),
104   mLoadRequest(false),
105   mLoadFailed(false),
106   mRasterized(false),
107   mKeepAnimation(false),
108   mLayerInfoCached(false),
109   mMarkerInfoCached(false),
110   mEnableFrameCache(false),
111   mNotifyAfterRasterization(false),
112   mSizeUpdated(false)
113 {
114   mVectorRenderer.UploadCompletedSignal().Connect(this, &VectorAnimationTask::OnUploadCompleted);
115 }
116
117 VectorAnimationTask::~VectorAnimationTask()
118 {
119   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::~VectorAnimationTask: destructor [%p]\n", this);
120 }
121
122 void VectorAnimationTask::Process()
123 {
124   mRasterized = Rasterize();
125 }
126
127 bool VectorAnimationTask::IsReady()
128 {
129   return true;
130 }
131
132 void VectorAnimationTask::Finalize()
133 {
134   {
135     Mutex::ScopedLock lock(mMutex);
136
137     // Release some objects in the main thread
138     if(mAnimationFinishedCallback)
139     {
140       mVectorAnimationThread.RemoveEventTriggerCallbacks(mAnimationFinishedCallback.get());
141       mAnimationFinishedCallback.reset();
142     }
143     if(mForceRenderOnceCallback)
144     {
145       mVectorAnimationThread.RemoveEventTriggerCallbacks(mForceRenderOnceCallback.get());
146       mForceRenderOnceCallback.reset();
147     }
148     if(mLoadCompletedCallback)
149     {
150       mVectorAnimationThread.RemoveEventTriggerCallbacks(mLoadCompletedCallback.get());
151       mLoadCompletedCallback.reset();
152     }
153
154     mDestroyTask = true;
155   }
156
157   mVectorRenderer.Finalize();
158 }
159
160 void VectorAnimationTask::TaskCompleted(VectorAnimationTaskPtr task)
161 {
162   mVectorAnimationThread.OnTaskCompleted(task, task->IsRasterized(), task->IsAnimating());
163 }
164
165 bool VectorAnimationTask::IsRasterized()
166 {
167   return mRasterized;
168 }
169
170 bool VectorAnimationTask::IsAnimating()
171 {
172   return mKeepAnimation;
173 }
174
175 bool VectorAnimationTask::Load(bool synchronousLoading)
176 {
177 #ifdef TRACE_ENABLED
178   uint64_t mStartTimeNanoSceonds = 0;
179   uint64_t mEndTimeNanoSceonds   = 0;
180   if(gTraceFilter && gTraceFilter->IsTraceEnabled())
181   {
182     mStartTimeNanoSceonds = GetNanoseconds();
183     std::ostringstream oss;
184     oss << "[u:" << mImageUrl.GetEllipsedUrl() << "]";
185     // DALI_TRACE_BEGIN(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
186     DALI_LOG_RELEASE_INFO("BEGIN: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str());
187   }
188 #endif
189
190   if(mEncodedImageBuffer)
191   {
192     if(!mVectorRenderer.Load(mEncodedImageBuffer.GetRawBuffer()))
193     {
194       mLoadFailed = true;
195     }
196
197     // We don't need to hold image buffer anymore.
198     mEncodedImageBuffer.Reset();
199   }
200   else if(mImageUrl.IsLocalResource())
201   {
202     if(!mVectorRenderer.Load(mImageUrl.GetUrl()))
203     {
204       mLoadFailed = true;
205     }
206   }
207   else
208   {
209     Dali::Vector<uint8_t> remoteData;
210     if(!Dali::FileLoader::DownloadFileSynchronously(mImageUrl.GetUrl(), remoteData) || // Failed if we fail to download json file,
211        !mVectorRenderer.Load(remoteData))                                              // or download data is not valid vector animation file.
212     {
213       mLoadFailed = true;
214     }
215   }
216
217   if(mLoadFailed)
218   {
219     DALI_LOG_ERROR("VectorAnimationTask::Load: Load failed [%s]\n", mImageUrl.GetUrl().c_str());
220     mLoadRequest = false;
221     {
222       Mutex::ScopedLock lock(mMutex);
223       if(!synchronousLoading && mLoadCompletedCallback)
224       {
225         mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
226       }
227     }
228 #ifdef TRACE_ENABLED
229     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
230     {
231       mEndTimeNanoSceonds = GetNanoseconds();
232       std::ostringstream oss;
233       oss << std::fixed << std::setprecision(3);
234       oss << "[";
235       oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
236       oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
237       // DALI_TRACE_END(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
238       DALI_LOG_RELEASE_INFO("END: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str());
239     }
240 #endif
241     return false;
242   }
243
244   mTotalFrame = mVectorRenderer.GetTotalFrameNumber();
245
246   mEndFrame = mTotalFrame - 1;
247
248   mFrameRate                 = mVectorRenderer.GetFrameRate();
249   mFrameDurationMicroSeconds = MICROSECONDS_PER_SECOND / mFrameRate;
250
251   mLoadRequest = false;
252   {
253     Mutex::ScopedLock lock(mMutex);
254     if(!synchronousLoading && mLoadCompletedCallback)
255     {
256       mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
257     }
258   }
259
260   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Load: file = %s [%d frames, %f fps] [%p]\n", mImageUrl.GetUrl().c_str(), mTotalFrame, mFrameRate, this);
261
262 #ifdef TRACE_ENABLED
263   if(gTraceFilter && gTraceFilter->IsTraceEnabled())
264   {
265     mEndTimeNanoSceonds = GetNanoseconds();
266     std::ostringstream oss;
267     oss << std::fixed << std::setprecision(3);
268     oss << "[";
269     oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
270     oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
271     // DALI_TRACE_END(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
272     DALI_LOG_RELEASE_INFO("END: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str());
273   }
274 #endif
275
276   return true;
277 }
278
279 void VectorAnimationTask::SetRenderer(Renderer renderer)
280 {
281   mVectorRenderer.SetRenderer(renderer);
282
283   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetRenderer [%p]\n", this);
284 }
285
286 void VectorAnimationTask::RequestLoad(const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, bool synchronousLoading)
287 {
288   mImageUrl           = url;
289   mEncodedImageBuffer = encodedImageBuffer;
290
291   if(!synchronousLoading)
292   {
293     mLoadRequest = true;
294
295     mVectorAnimationThread.AddTask(this);
296   }
297   else
298   {
299     Load(true);
300
301     OnLoadCompleted(0u);
302   }
303 }
304
305 bool VectorAnimationTask::IsLoadRequested() const
306 {
307   return mLoadRequest;
308 }
309
310 void VectorAnimationTask::SetAnimationData(const AnimationData& data)
311 {
312   Mutex::ScopedLock lock(mMutex);
313
314   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetAnimationData [%p]\n", this);
315
316   uint32_t index = mAnimationDataIndex == 0 ? 1 : 0; // Use the other buffer
317
318   mAnimationData[index].push_back(data);
319   mAnimationDataUpdated = true;
320
321   if(data.resendFlag & VectorAnimationTask::RESEND_SIZE)
322   {
323     // The size should be changed in the main thread.
324     SetSize(data.width, data.height);
325   }
326
327   mVectorAnimationThread.AddTask(this);
328 }
329
330 void VectorAnimationTask::SetSize(uint32_t width, uint32_t height)
331 {
332   if(mWidth != width || mHeight != height)
333   {
334     mVectorRenderer.SetSize(width, height);
335
336     mWidth  = width;
337     mHeight = height;
338
339     // If fixedCache is enabled, Call KeepRasterizedBuffer()
340     if(mEnableFrameCache)
341     {
342       if(mTotalFrame > 0 && !mLoadFailed)
343       {
344         mVectorRenderer.KeepRasterizedBuffer();
345       }
346       else
347       {
348         // If Load is not yet, update the size later.
349         mSizeUpdated = true;
350       }
351     }
352
353     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetSize: width = %d, height = %d [%p]\n", width, height, this);
354   }
355 }
356
357 void VectorAnimationTask::PlayAnimation()
358 {
359   if(mPlayState != PlayState::PLAYING)
360   {
361     mNeedAnimationFinishedTrigger = true;
362     mUpdateFrameNumber            = false;
363     mPlayState                    = PlayState::PLAYING;
364
365     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::PlayAnimation: Play [%p]\n", this);
366   }
367 }
368
369 void VectorAnimationTask::StopAnimation()
370 {
371   if(mPlayState != PlayState::STOPPING)
372   {
373     mNeedAnimationFinishedTrigger = false;
374     mPlayState                    = PlayState::STOPPING;
375
376     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::StopAnimation: Stop [%p]\n", this);
377   }
378 }
379
380 void VectorAnimationTask::PauseAnimation()
381 {
382   if(mPlayState == PlayState::PLAYING)
383   {
384     mPlayState = PlayState::PAUSED;
385
386     // Ensure to render paused frame.
387     mNeedForceRenderOnceTrigger = true;
388
389     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::PauseAnimation: Pause [%p]\n", this);
390   }
391 }
392
393 void VectorAnimationTask::SetAnimationFinishedCallback(CallbackBase* callback)
394 {
395   Mutex::ScopedLock lock(mMutex);
396   mAnimationFinishedCallback = std::unique_ptr<CallbackBase>(callback);
397 }
398
399 void VectorAnimationTask::SetForceRenderOnceCallback(CallbackBase* callback)
400 {
401   Mutex::ScopedLock lock(mMutex);
402   mForceRenderOnceCallback = std::unique_ptr<CallbackBase>(callback);
403 }
404
405 void VectorAnimationTask::SetLoopCount(int32_t count)
406 {
407   if(mLoopCount != count)
408   {
409     mLoopCount   = count;
410     mCurrentLoop = 0;
411
412     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetLoopCount: [%d] [%p]\n", count, this);
413   }
414 }
415
416 void VectorAnimationTask::SetPlayRange(const Property::Array& playRange)
417 {
418   bool     valid      = false;
419   uint32_t startFrame = 0, endFrame = 0;
420   size_t   count = playRange.Count();
421
422   if(count >= 2)
423   {
424     int32_t start = 0, end = 0;
425     if(playRange.GetElementAt(0).Get(start) && playRange.GetElementAt(1).Get(end))
426     {
427       startFrame = static_cast<uint32_t>(start);
428       endFrame   = static_cast<uint32_t>(end);
429       valid      = true;
430     }
431     else
432     {
433       std::string startMarker, endMarker;
434       if(playRange.GetElementAt(0).Get(startMarker) && playRange.GetElementAt(1).Get(endMarker))
435       {
436         if(mVectorRenderer)
437         {
438           uint32_t frame; // We don't use this later
439           if(mVectorRenderer.GetMarkerInfo(startMarker, startFrame, frame) && mVectorRenderer.GetMarkerInfo(endMarker, frame, endFrame))
440           {
441             valid = true;
442           }
443         }
444       }
445     }
446   }
447   else if(count == 1)
448   {
449     std::string marker;
450     if(playRange.GetElementAt(0).Get(marker))
451     {
452       if(mVectorRenderer && mVectorRenderer.GetMarkerInfo(marker, startFrame, endFrame))
453       {
454         valid = true;
455       }
456     }
457   }
458
459   if(!valid)
460   {
461     DALI_LOG_ERROR("VectorAnimationTask::SetPlayRange: Invalid range [%p]\n", this);
462     return;
463   }
464
465   // Make sure the range specified is between 0 and the total frame number
466   startFrame = std::min(startFrame, mTotalFrame - 1);
467   endFrame   = std::min(endFrame, mTotalFrame - 1);
468
469   // If the range is not in order swap values
470   if(startFrame > endFrame)
471   {
472     uint32_t temp = startFrame;
473     startFrame    = endFrame;
474     endFrame      = temp;
475   }
476
477   if(startFrame != mStartFrame || endFrame != mEndFrame)
478   {
479     mStartFrame = startFrame;
480     mEndFrame   = endFrame;
481
482     // If the current frame is out of the range, change the current frame also.
483     if(mStartFrame > mCurrentFrame)
484     {
485       mCurrentFrame = mStartFrame;
486
487       if(mPlayState != PlayState::PLAYING)
488       {
489         // Ensure to render current frame.
490         mNeedForceRenderOnceTrigger = true;
491       }
492     }
493     else if(mEndFrame < mCurrentFrame)
494     {
495       mCurrentFrame = mEndFrame;
496
497       if(mPlayState != PlayState::PLAYING)
498       {
499         // Ensure to render current frame.
500         mNeedForceRenderOnceTrigger = true;
501       }
502     }
503
504     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetPlayRange: [%d, %d] [%s] [%p]\n", mStartFrame, mEndFrame, mImageUrl.GetUrl().c_str(), this);
505   }
506 }
507
508 void VectorAnimationTask::GetPlayRange(uint32_t& startFrame, uint32_t& endFrame)
509 {
510   startFrame = mStartFrame;
511   endFrame   = mEndFrame;
512 }
513
514 void VectorAnimationTask::SetCurrentFrameNumber(uint32_t frameNumber)
515 {
516   if(mCurrentFrame == frameNumber)
517   {
518     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetCurrentFrameNumber: Set same frame [%d] [%p]\n", frameNumber, this);
519     return;
520   }
521
522   if(frameNumber >= mStartFrame && frameNumber <= mEndFrame)
523   {
524     mCurrentFrame      = frameNumber;
525     mUpdateFrameNumber = false;
526
527     if(mPlayState != PlayState::PLAYING)
528     {
529       // Ensure to render current frame.
530       mNeedForceRenderOnceTrigger = true;
531     }
532
533     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetCurrentFrameNumber: frame number = %d [%p]\n", mCurrentFrame, this);
534   }
535   else
536   {
537     DALI_LOG_ERROR("Invalid frame number [%d (%d, %d)] [%p]\n", frameNumber, mStartFrame, mEndFrame, this);
538   }
539 }
540
541 uint32_t VectorAnimationTask::GetCurrentFrameNumber() const
542 {
543   return mCurrentFrame;
544 }
545
546 uint32_t VectorAnimationTask::GetTotalFrameNumber() const
547 {
548   return mTotalFrame;
549 }
550
551 void VectorAnimationTask::GetDefaultSize(uint32_t& width, uint32_t& height) const
552 {
553   mVectorRenderer.GetDefaultSize(width, height);
554 }
555
556 void VectorAnimationTask::SetStopBehavior(DevelImageVisual::StopBehavior::Type stopBehavior)
557 {
558   mStopBehavior = stopBehavior;
559
560   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetStopBehavior: stop behavor = %d [%p]\n", mStopBehavior, this);
561 }
562
563 void VectorAnimationTask::SetLoopingMode(DevelImageVisual::LoopingMode::Type loopingMode)
564 {
565   mLoopingMode = loopingMode;
566
567   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::SetLoopingMode: looping mode = %d [%p]\n", mLoopingMode, this);
568 }
569
570 void VectorAnimationTask::GetLayerInfo(Property::Map& map) const
571 {
572   // Fast-out if file is loading, or load failed.
573   if(mLoadFailed || IsLoadRequested())
574   {
575     return;
576   }
577
578   if(DALI_UNLIKELY(!mLayerInfoCached))
579   {
580     // Update only 1 time.
581     mLayerInfoCached = true;
582     mVectorRenderer.GetLayerInfo(mCachedLayerInfo);
583   }
584
585   map = mCachedLayerInfo;
586 }
587
588 void VectorAnimationTask::GetMarkerInfo(Property::Map& map) const
589 {
590   // Fast-out if file is loading, or load failed.
591   if(mLoadFailed || IsLoadRequested())
592   {
593     return;
594   }
595
596   if(DALI_UNLIKELY(!mMarkerInfoCached))
597   {
598     // Update only 1 time.
599     mMarkerInfoCached = true;
600     mVectorRenderer.GetMarkerInfo(mCachedMarkerInfo);
601   }
602
603   map = mCachedMarkerInfo;
604 }
605
606 VectorAnimationTask::ResourceReadySignalType& VectorAnimationTask::ResourceReadySignal()
607 {
608   return mResourceReadySignal;
609 }
610
611 bool VectorAnimationTask::Rasterize()
612 {
613   bool     stopped = false;
614   uint32_t currentFrame;
615   mKeepAnimation = false;
616
617   {
618     Mutex::ScopedLock lock(mMutex);
619     if(mDestroyTask)
620     {
621       // The task will be destroyed. We don't need rasterization.
622       return false;
623     }
624   }
625
626   if(mLoadRequest)
627   {
628     return Load(false);
629   }
630
631   if(mLoadFailed)
632   {
633     return false;
634   }
635
636 #ifdef TRACE_ENABLED
637   uint64_t mStartTimeNanoSceonds = 0;
638   uint64_t mEndTimeNanoSceonds   = 0;
639 #endif
640   DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_LOTTIE_RASTERIZE_TASK", [&](std::ostringstream& oss) {
641     mStartTimeNanoSceonds = GetNanoseconds();
642     oss << "[s:" << mWidth << "x" << mHeight << " ";
643     oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
644   });
645
646   ApplyAnimationData();
647
648   if(mPlayState == PlayState::PLAYING && mUpdateFrameNumber)
649   {
650     mCurrentFrame = mForward ? mCurrentFrame + mDroppedFrames + 1 : (mCurrentFrame > mDroppedFrames ? mCurrentFrame - mDroppedFrames - 1 : 0);
651     Dali::ClampInPlace(mCurrentFrame, mStartFrame, mEndFrame);
652   }
653
654   currentFrame = mCurrentFrame;
655
656   mUpdateFrameNumber = true;
657
658   if(mPlayState == PlayState::STOPPING)
659   {
660     mCurrentFrame = GetStoppedFrame(mStartFrame, mEndFrame, mCurrentFrame);
661     currentFrame  = mCurrentFrame;
662     stopped       = true;
663   }
664   else if(mPlayState == PlayState::PLAYING)
665   {
666     bool animationFinished = false;
667
668     if(currentFrame >= mEndFrame) // last frame
669     {
670       if(mLoopingMode == DevelImageVisual::LoopingMode::AUTO_REVERSE)
671       {
672         mForward = false;
673       }
674       else
675       {
676         if(mLoopCount < 0 || ++mCurrentLoop < mLoopCount) // repeat forever or before the last loop
677         {
678           mCurrentFrame      = mStartFrame;
679           mUpdateFrameNumber = false;
680         }
681         else
682         {
683           animationFinished = true; // end of animation
684         }
685       }
686     }
687     else if(currentFrame == mStartFrame && !mForward) // first frame
688     {
689       if(mLoopCount < 0 || ++mCurrentLoop < mLoopCount) // repeat forever or before the last loop
690       {
691         mForward = true;
692       }
693       else
694       {
695         animationFinished = true; // end of animation
696       }
697     }
698
699     if(animationFinished)
700     {
701       if(mStopBehavior == DevelImageVisual::StopBehavior::CURRENT_FRAME)
702       {
703         stopped = true;
704       }
705       else
706       {
707         mPlayState = PlayState::STOPPING;
708       }
709     }
710   }
711
712   // Rasterize
713   bool renderSuccess = false;
714   if(mVectorRenderer)
715   {
716     renderSuccess = mVectorRenderer.Render(currentFrame);
717     if(!renderSuccess)
718     {
719       DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Rasterize: Rendering failed. Try again later.[%d] [%p]\n", currentFrame, this);
720       mUpdateFrameNumber = false;
721     }
722   }
723
724   if(stopped && renderSuccess)
725   {
726     mPlayState   = PlayState::STOPPED;
727     mForward     = true;
728     mCurrentLoop = 0;
729
730     mNeedForceRenderOnceTrigger = true;
731
732     if(mVectorRenderer)
733     {
734       // Notify the Renderer that rendering is stopped.
735       mVectorRenderer.RenderStopped();
736     }
737
738     // Animation is finished
739     {
740       Mutex::ScopedLock lock(mMutex);
741       if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback)
742       {
743         mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get(), mAppliedPlayStateId);
744       }
745     }
746
747     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Rasterize: Animation is finished [current = %d] [%p]\n", currentFrame, this);
748   }
749
750   // Forcely trigger render once if need.
751   if(mNotifyAfterRasterization || mNeedForceRenderOnceTrigger)
752   {
753     Mutex::ScopedLock lock(mMutex);
754     if(mForceRenderOnceCallback)
755     {
756       mVectorAnimationThread.AddEventTriggerCallback(mForceRenderOnceCallback.get(), mAppliedPlayStateId);
757     }
758     mNeedForceRenderOnceTrigger = false;
759   }
760
761   if(mPlayState != PlayState::PAUSED && mPlayState != PlayState::STOPPED)
762   {
763     mKeepAnimation = true;
764   }
765
766   DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_LOTTIE_RASTERIZE_TASK", [&](std::ostringstream& oss) {
767     mEndTimeNanoSceonds = GetNanoseconds();
768     oss << std::fixed << std::setprecision(3);
769     oss << "[";
770     oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
771     oss << "s:" << mWidth << "x" << mHeight << " ";
772     oss << "f:" << mCurrentFrame << " ";
773     oss << "l:" << mCurrentLoop << " ";
774     oss << "p:" << mPlayState << " ";
775     oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
776   });
777
778   return true;
779 }
780
781 uint32_t VectorAnimationTask::GetStoppedFrame(uint32_t startFrame, uint32_t endFrame, uint32_t currentFrame)
782 {
783   uint32_t frame = currentFrame;
784
785   switch(mStopBehavior)
786   {
787     case DevelImageVisual::StopBehavior::FIRST_FRAME:
788     {
789       frame = startFrame;
790       break;
791     }
792     case DevelImageVisual::StopBehavior::LAST_FRAME:
793     {
794       if(mLoopingMode == DevelImageVisual::LoopingMode::AUTO_REVERSE)
795       {
796         frame = startFrame;
797       }
798       else
799       {
800         frame = endFrame;
801       }
802       break;
803     }
804     case DevelImageVisual::StopBehavior::CURRENT_FRAME:
805     {
806       frame = currentFrame;
807       break;
808     }
809   }
810
811   return frame;
812 }
813
814 VectorAnimationTask::TimePoint VectorAnimationTask::CalculateNextFrameTime(bool renderNow)
815 {
816   // std::chrono::time_point template has second parameter duration which defaults to the std::chrono::steady_clock supported
817   // duration. In some C++11 implementations it is a milliseconds duration, so it fails to compile unless mNextFrameStartTime
818   // is casted to use the default duration.
819   mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds));
820   auto current        = std::chrono::steady_clock::now();
821   mDroppedFrames      = 0;
822
823   if(renderNow)
824   {
825     mNextFrameStartTime = current;
826   }
827   else if(mNextFrameStartTime < current)
828   {
829     uint32_t droppedFrames = 0;
830
831     while(current > std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds)) && droppedFrames < mTotalFrame)
832     {
833       droppedFrames++;
834       mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds));
835     }
836
837     mNextFrameStartTime = current;
838     mDroppedFrames      = droppedFrames;
839   }
840
841   return mNextFrameStartTime;
842 }
843
844 VectorAnimationTask::TimePoint VectorAnimationTask::GetNextFrameTime()
845 {
846   return mNextFrameStartTime;
847 }
848
849 void VectorAnimationTask::ApplyAnimationData()
850 {
851   uint32_t index;
852
853   {
854     Mutex::ScopedLock lock(mMutex);
855
856     if(!mAnimationDataUpdated || mAnimationData[mAnimationDataIndex].size() != 0)
857     {
858       // Data is not updated or the previous data is not applied yet.
859       return;
860     }
861
862     mAnimationDataIndex   = mAnimationDataIndex == 0 ? 1 : 0; // Swap index
863     mAnimationDataUpdated = false;
864
865     index = mAnimationDataIndex;
866   }
867
868   for(const auto& animationData : mAnimationData[index])
869   {
870     if(animationData.resendFlag & VectorAnimationTask::RESEND_LOOP_COUNT)
871     {
872       SetLoopCount(animationData.loopCount);
873     }
874
875     if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_RANGE)
876     {
877       SetPlayRange(animationData.playRange);
878     }
879
880     if(animationData.resendFlag & VectorAnimationTask::RESEND_STOP_BEHAVIOR)
881     {
882       SetStopBehavior(animationData.stopBehavior);
883     }
884
885     if(animationData.resendFlag & VectorAnimationTask::RESEND_LOOPING_MODE)
886     {
887       SetLoopingMode(animationData.loopingMode);
888     }
889
890     if(animationData.resendFlag & VectorAnimationTask::RESEND_CURRENT_FRAME)
891     {
892       SetCurrentFrameNumber(animationData.currentFrame);
893     }
894
895     if(animationData.resendFlag & VectorAnimationTask::RESEND_NOTIFY_AFTER_RASTERIZATION)
896     {
897       mNotifyAfterRasterization = animationData.notifyAfterRasterization;
898     }
899
900     if(animationData.resendFlag & VectorAnimationTask::RESEND_NEED_RESOURCE_READY)
901     {
902       mVectorRenderer.InvalidateBuffer();
903     }
904
905     if(animationData.resendFlag & VectorAnimationTask::RESEND_DYNAMIC_PROPERTY)
906     {
907       for(auto&& iter : animationData.dynamicProperties)
908       {
909         mVectorRenderer.AddPropertyValueCallback(iter.keyPath, static_cast<VectorAnimationRenderer::VectorProperty>(iter.property), iter.callback, iter.id);
910       }
911     }
912
913     if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
914     {
915       mAppliedPlayStateId = animationData.playStateId;
916       if(animationData.playState == DevelImageVisual::PlayState::PLAYING)
917       {
918         PlayAnimation();
919       }
920       else if(animationData.playState == DevelImageVisual::PlayState::PAUSED)
921       {
922         PauseAnimation();
923       }
924       else if(animationData.playState == DevelImageVisual::PlayState::STOPPED)
925       {
926         StopAnimation();
927       }
928     }
929   }
930
931   // reset data list
932   mAnimationData[index].clear();
933 }
934
935 void VectorAnimationTask::OnUploadCompleted()
936 {
937   mResourceReadySignal.Emit(ResourceStatus::READY);
938 }
939
940 void VectorAnimationTask::OnLoadCompleted(uint32_t /* not used */)
941 {
942   if(!mLoadFailed)
943   {
944     if(mEnableFrameCache && mSizeUpdated)
945     {
946       mVectorRenderer.KeepRasterizedBuffer();
947       mSizeUpdated = false;
948     }
949     mResourceReadySignal.Emit(ResourceStatus::LOADED);
950   }
951   else
952   {
953     mResourceReadySignal.Emit(ResourceStatus::FAILED);
954   }
955 }
956 } // namespace Internal
957
958 } // namespace Toolkit
959
960 } // namespace Dali