[4.0] (AnimatedVectorImageVisual) Render frames based on content's fps
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-vector-image / vector-rasterize-thread.cpp
1 /*
2  * Copyright (c) 2018 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-rasterize-thread.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/adaptors/adaptor.h>
23 #include <dali/integration-api/debug.h>
24 #include <chrono>
25 #include <thread>
26
27 namespace Dali
28 {
29
30 namespace Toolkit
31 {
32
33 namespace Internal
34 {
35
36 namespace
37 {
38
39 constexpr auto LOOP_FOREVER = -1;
40 constexpr auto NANOSECONDS_PER_SECOND( 1e+9 );
41
42 #if defined(DEBUG_ENABLED)
43 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_VECTOR_ANIMATION" );
44 #endif
45
46 } // unnamed namespace
47
48 VectorRasterizeThread::VectorRasterizeThread( const std::string& url )
49 : mUrl( url ),
50   mVectorRenderer(),
51   mConditionalWait(),
52   mMutex(),
53   mResourceReadyTrigger(),
54   mAnimationFinishedTrigger(),
55   mPlayRange( 0.0f, 1.0f ),
56   mPlayState( DevelImageVisual::PlayState::STOPPED ),
57   mFrameDurationNanoSeconds( 0 ),
58   mProgress( 0.0f ),
59   mFrameRate( 60.0f ),
60   mCurrentFrame( 0 ),
61   mTotalFrame( 0 ),
62   mStartFrame( 0 ),
63   mEndFrame( 0 ),
64   mWidth( 0 ),
65   mHeight( 0 ),
66   mLoopCount( LOOP_FOREVER ),
67   mCurrentLoop( 0 ),
68   mNeedRender( false ),
69   mDestroyThread( false ),
70   mResourceReady( false ),
71   mLogFactory( Dali::Adaptor::Get().GetLogFactory() )
72 {
73   mVectorRenderer = VectorAnimationRenderer::New( mUrl );
74 }
75
76 VectorRasterizeThread::~VectorRasterizeThread()
77 {
78   // Stop the thread
79   {
80     ConditionalWait::ScopedLock lock( mConditionalWait );
81     mDestroyThread = true;
82     mConditionalWait.Notify( lock );
83
84     // This should be called in the main thread to stop waiting for the dequeuable buffer.
85     mVectorRenderer.StopRender();
86   }
87
88   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::~VectorRasterizeThread: Join\n" );
89
90   Join();
91 }
92
93 void VectorRasterizeThread::Run()
94 {
95   mLogFactory.InstallLogFunction();
96
97   //TODO: check the return value
98   StartRender();
99
100   while( IsThreadReady() )
101   {
102     Rasterize();
103   }
104 }
105
106 void VectorRasterizeThread::SetRenderer( Renderer renderer )
107 {
108   ConditionalWait::ScopedLock lock( mConditionalWait );
109
110   mVectorRenderer.SetRenderer( renderer );
111
112   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetRenderer\n" );
113 }
114
115 void VectorRasterizeThread::SetSize( uint32_t width, uint32_t height )
116 {
117   if( mWidth != width || mHeight != height )
118   {
119     ConditionalWait::ScopedLock lock( mConditionalWait );
120     mVectorRenderer.SetSize( width, height );
121
122     mWidth = width;
123     mHeight = height;
124
125     mResourceReady = false;
126
127     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetSize: width = %d, height = %d\n", width, height );
128   }
129 }
130
131 void VectorRasterizeThread::PlayAnimation()
132 {
133   ConditionalWait::ScopedLock lock( mConditionalWait );
134   if( mPlayState != DevelImageVisual::PlayState::PLAYING )
135   {
136     mPlayState = DevelImageVisual::PlayState::PLAYING;
137     mConditionalWait.Notify( lock );
138
139     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::PlayAnimation: Start\n" );
140   }
141 }
142
143 void VectorRasterizeThread::StopAnimation()
144 {
145   ConditionalWait::ScopedLock lock( mConditionalWait );
146   if( mPlayState != DevelImageVisual::PlayState::STOPPED )
147   {
148     mPlayState = DevelImageVisual::PlayState::STOPPED;
149
150     // Reset the current frame and the current loop
151     mCurrentFrame = mStartFrame;
152     mCurrentLoop = 0;
153
154     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::StopAnimation: Stop\n" );
155   }
156 }
157
158 void VectorRasterizeThread::PauseAnimation()
159 {
160   ConditionalWait::ScopedLock lock( mConditionalWait );
161   if( mPlayState == DevelImageVisual::PlayState::PLAYING )
162   {
163     mPlayState = DevelImageVisual::PlayState::PAUSED;
164
165     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::PauseAnimation: Pause\n" );
166   }
167 }
168
169 void VectorRasterizeThread::RenderFrame()
170 {
171   ConditionalWait::ScopedLock lock( mConditionalWait );
172
173   if( !mResourceReady )
174   {
175     mNeedRender = true;
176     mConditionalWait.Notify( lock );
177
178     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::RenderFrame: Render\n" );
179   }
180 }
181
182 void VectorRasterizeThread::SetResourceReadyCallback( EventThreadCallback* callback )
183 {
184   ConditionalWait::ScopedLock lock( mConditionalWait );
185   mResourceReadyTrigger = std::unique_ptr< EventThreadCallback >( callback );
186 }
187
188 void VectorRasterizeThread::SetAnimationFinishedCallback( EventThreadCallback* callback )
189 {
190   ConditionalWait::ScopedLock lock( mConditionalWait );
191   mAnimationFinishedTrigger = std::unique_ptr< EventThreadCallback >( callback );
192 }
193
194 void VectorRasterizeThread::SetLoopCount( int32_t count )
195 {
196   if( mLoopCount != count )
197   {
198     ConditionalWait::ScopedLock lock( mConditionalWait );
199
200     mLoopCount = count;
201
202     // Reset progress
203     mCurrentLoop = 0;
204   }
205 }
206
207 int32_t VectorRasterizeThread::GetLoopCount() const
208 {
209   return mLoopCount;
210 }
211
212 void VectorRasterizeThread::SetPlayRange( Vector2 range )
213 {
214   // Make sure the range specified is between 0.0 and 1.0
215   if( range.x >= 0.0f && range.x <= 1.0f && range.y >= 0.0f && range.y <= 1.0f )
216   {
217     Vector2 orderedRange( range );
218     // If the range is not in order swap values
219     if( range.x > range.y )
220     {
221       orderedRange = Vector2( range.y, range.x );
222     }
223
224     if( mPlayRange != orderedRange )
225     {
226       ConditionalWait::ScopedLock lock( mConditionalWait );
227
228       mPlayRange = orderedRange;
229
230       if( mTotalFrame != 0 )
231       {
232         mStartFrame = static_cast< uint32_t >( mPlayRange.x * mTotalFrame + 0.5f );
233         mEndFrame = static_cast< uint32_t >( mPlayRange.y * mTotalFrame + 0.5f );
234
235         // If the current frame is out of the range, change the current frame also.
236         if( mStartFrame > mCurrentFrame )
237         {
238           mCurrentFrame = mStartFrame;
239
240           mResourceReady = false;
241         }
242         else if( mEndFrame < mCurrentFrame )
243         {
244           mCurrentFrame = mEndFrame;
245
246           mResourceReady = false;
247         }
248       }
249     }
250   }
251 }
252
253 Vector2 VectorRasterizeThread::GetPlayRange() const
254 {
255   return mPlayRange;
256 }
257
258 void VectorRasterizeThread::SetCurrentProgress( float progress )
259 {
260   ConditionalWait::ScopedLock lock( mConditionalWait );
261
262   if( progress >= mPlayRange.x && progress <= mPlayRange.y )
263   {
264     mProgress = progress;
265
266     if( mTotalFrame != 0 )
267     {
268       mCurrentFrame = static_cast< uint32_t >( mTotalFrame * progress + 0.5f );
269     }
270
271     mResourceReady = false;
272
273     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetCurrentProgress: progress = %f (%d)\n", progress, mCurrentFrame );
274   }
275 }
276
277 float VectorRasterizeThread::GetCurrentProgress() const
278 {
279   return ( static_cast< float >( mCurrentFrame ) / static_cast< float >( mTotalFrame ) );
280 }
281
282 DevelImageVisual::PlayState VectorRasterizeThread::GetPlayState() const
283 {
284   return mPlayState;
285 }
286
287 bool VectorRasterizeThread::IsResourceReady() const
288 {
289   return mResourceReady;
290 }
291
292 bool VectorRasterizeThread::IsThreadReady()
293 {
294   ConditionalWait::ScopedLock lock( mConditionalWait );
295
296   if( mPlayState != DevelImageVisual::PlayState::PLAYING && !mNeedRender && !mDestroyThread )
297   {
298     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::IsThreadReady: Wait\n" );
299
300     mConditionalWait.Wait( lock );
301   }
302
303   // Keep the thread alive if this thread is NOT to be destroyed
304   return !mDestroyThread;
305 }
306
307 bool VectorRasterizeThread::StartRender()
308 {
309   //TODO: check the return value
310   mVectorRenderer.StartRender();
311
312   mTotalFrame = mVectorRenderer.GetTotalFrameNumber();
313
314   mStartFrame = static_cast< uint32_t >( mPlayRange.x * mTotalFrame + 0.5f );
315   mEndFrame = static_cast< uint32_t >( mPlayRange.y * mTotalFrame + 0.5f );
316
317   mCurrentFrame = std::max( static_cast< uint32_t >( mTotalFrame * mProgress + 0.5f ), mStartFrame );
318
319   mFrameRate = mVectorRenderer.GetFrameRate();
320   mFrameDurationNanoSeconds = NANOSECONDS_PER_SECOND / mFrameRate;
321
322   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::StartRender: Renderer is started [%d, %f fps]\n", mTotalFrame, mFrameRate );
323
324   return true;
325 }
326
327 void VectorRasterizeThread::Rasterize()
328 {
329   bool needRender, resourceReady;
330
331   {
332     ConditionalWait::ScopedLock lock( mConditionalWait );
333     needRender = mNeedRender;
334     resourceReady = mResourceReady;
335   }
336
337   auto currentFrameStartTime = std::chrono::system_clock::now();
338
339   // Rasterize
340   mVectorRenderer.Render( mCurrentFrame );
341
342   if( mPlayState == DevelImageVisual::PlayState::PLAYING )
343   {
344     if( ++mCurrentFrame >= mEndFrame )
345     {
346       if( mLoopCount < 0 )
347       {
348         // repeat forever
349         mCurrentFrame = mStartFrame;
350       }
351       else
352       {
353         mCurrentLoop++;
354         if( mCurrentLoop >= mLoopCount )
355         {
356           // Animation is finished
357           mPlayState = DevelImageVisual::PlayState::STOPPED;
358
359           // Reset the current frame and the current loop
360           mCurrentFrame = mStartFrame;
361           mCurrentLoop = 0;
362
363           mAnimationFinishedTrigger->Trigger();
364
365           DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Animation is finished\n" );
366         }
367         else
368         {
369           mCurrentFrame = mStartFrame;
370         }
371       }
372     }
373   }
374
375   if( needRender )
376   {
377     mNeedRender = false;
378   }
379
380   if( !resourceReady )
381   {
382     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Resource ready trigger\n" );
383
384     mResourceReadyTrigger->Trigger();
385     mResourceReady = true;
386   }
387
388   auto timeToSleepUntil = currentFrameStartTime + std::chrono::nanoseconds( mFrameDurationNanoSeconds );
389
390 #if defined(DEBUG_ENABLED)
391   auto sleepDuration = std::chrono::duration_cast< std::chrono::milliseconds >( timeToSleepUntil - std::chrono::system_clock::now() );
392
393   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: [current = %d, sleep duration = %lld]\n", mCurrentFrame, sleepDuration.count() );
394 #endif
395
396   std::this_thread::sleep_until( timeToSleepUntil );
397 }
398
399 } // namespace Internal
400
401 } // namespace Toolkit
402
403 } // namespace Dali