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