Merge "(AnimatedVectorImage) Ensure the progress is kept according to actions" into...
[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   mVectorRenderer = VectorAnimationRenderer::New( mUrl );
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   //TODO: check the return value
109   StartRender();
110
111   while( !mDestroyThread )
112   {
113     Rasterize();
114   }
115 }
116
117 void VectorRasterizeThread::SetRenderer( Renderer renderer )
118 {
119   ConditionalWait::ScopedLock lock( mConditionalWait );
120
121   mVectorRenderer.SetRenderer( renderer );
122
123   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetRenderer\n" );
124 }
125
126 void VectorRasterizeThread::SetSize( uint32_t width, uint32_t height )
127 {
128   if( mWidth != width || mHeight != height )
129   {
130     ConditionalWait::ScopedLock lock( mConditionalWait );
131     mVectorRenderer.SetSize( width, height );
132
133     mWidth = width;
134     mHeight = height;
135
136     mResourceReady = false;
137
138     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetSize: width = %d, height = %d\n", width, height );
139   }
140 }
141
142 void VectorRasterizeThread::PlayAnimation()
143 {
144   ConditionalWait::ScopedLock lock( mConditionalWait );
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 }
213
214 int32_t VectorRasterizeThread::GetLoopCount() const
215 {
216   return mLoopCount;
217 }
218
219 void VectorRasterizeThread::SetPlayRange( Vector2 range )
220 {
221   // Make sure the range specified is between 0.0 and 1.0
222   if( range.x >= 0.0f && range.x <= 1.0f && range.y >= 0.0f && range.y <= 1.0f )
223   {
224     Vector2 orderedRange( range );
225     // If the range is not in order swap values
226     if( range.x > range.y )
227     {
228       orderedRange = Vector2( range.y, range.x );
229     }
230
231     if( mPlayRange != orderedRange )
232     {
233       ConditionalWait::ScopedLock lock( mConditionalWait );
234
235       mPlayRange = orderedRange;
236
237       if( mTotalFrame != 0 )
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     }
259   }
260 }
261
262 Vector2 VectorRasterizeThread::GetPlayRange() const
263 {
264   return mPlayRange;
265 }
266
267 void VectorRasterizeThread::SetCurrentProgress( float progress )
268 {
269   ConditionalWait::ScopedLock lock( mConditionalWait );
270
271   if( progress >= mPlayRange.x && progress <= mPlayRange.y )
272   {
273     mProgress = progress;
274
275     if( mTotalFrame != 0 )
276     {
277       mCurrentFrame = static_cast< uint32_t >( mTotalFrame * progress + 0.5f );
278       mCurrentFrameUpdated = true;
279     }
280
281     mResourceReady = false;
282
283     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::SetCurrentProgress: progress = %f (%d)\n", progress, mCurrentFrame );
284   }
285 }
286
287 float VectorRasterizeThread::GetCurrentProgress() const
288 {
289   return ( static_cast< float >( mCurrentFrame ) / static_cast< float >( mTotalFrame ) );
290 }
291
292 DevelImageVisual::PlayState VectorRasterizeThread::GetPlayState() const
293 {
294   return mPlayState;
295 }
296
297 bool VectorRasterizeThread::IsResourceReady() const
298 {
299   return mResourceReady;
300 }
301
302 bool VectorRasterizeThread::StartRender()
303 {
304   //TODO: check the return value
305   mVectorRenderer.StartRender();
306
307   mTotalFrame = mVectorRenderer.GetTotalFrameNumber();
308
309   mStartFrame = static_cast< uint32_t >( mPlayRange.x * mTotalFrame + 0.5f );
310   mEndFrame = static_cast< uint32_t >( mPlayRange.y * mTotalFrame + 0.5f );
311
312   mCurrentFrame = std::max( static_cast< uint32_t >( mTotalFrame * mProgress + 0.5f ), mStartFrame );
313
314   mFrameRate = mVectorRenderer.GetFrameRate();
315   mFrameDurationNanoSeconds = NANOSECONDS_PER_SECOND / mFrameRate;
316
317   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::StartRender: Renderer is started [%d, %f fps]\n", mTotalFrame, mFrameRate );
318
319   return true;
320 }
321
322 void VectorRasterizeThread::Rasterize()
323 {
324   bool resourceReady;
325   uint32_t currentFrame, startFrame, endFrame;
326   int32_t loopCount;
327   DevelImageVisual::PlayState playState;
328
329   {
330     ConditionalWait::ScopedLock lock( mConditionalWait );
331
332     if( mPlayState != DevelImageVisual::PlayState::PLAYING && !mNeedRender && !mDestroyThread )
333     {
334       DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Wait\n" );
335
336       if( mPlayState == DevelImageVisual::PlayState::STOPPED )
337       {
338         // Reset the current loop
339         mCurrentLoop = 0;
340       }
341       mConditionalWait.Wait( lock );
342     }
343
344     resourceReady = mResourceReady;
345     currentFrame = mCurrentFrame++;
346     startFrame = mStartFrame;
347     endFrame = mEndFrame;
348     loopCount = mLoopCount;
349     playState = mPlayState;
350
351     mNeedRender = false;
352     mResourceReady = true;
353     mCurrentFrameUpdated = false;
354   }
355
356   auto currentFrameStartTime = std::chrono::system_clock::now();
357
358   // Rasterize
359   mVectorRenderer.Render( currentFrame );
360
361   if( playState == DevelImageVisual::PlayState::PLAYING )
362   {
363     if( currentFrame >= endFrame )
364     {
365       if( loopCount < 0 )
366       {
367         // repeat forever
368         ResetToStart( mCurrentFrameUpdated, mCurrentFrame, startFrame, mConditionalWait );  // If the current frame is changed in the event thread, don't overwrite it.
369       }
370       else
371       {
372         mCurrentLoop++;
373         if( mCurrentLoop >= loopCount )
374         {
375           mPlayState = DevelImageVisual::PlayState::STOPPED;
376
377           // Animation is finished
378           mAnimationFinishedTrigger->Trigger();
379
380           DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Animation is finished\n" );
381         }
382         else
383         {
384           ResetToStart( mCurrentFrameUpdated, mCurrentFrame, startFrame, mConditionalWait );
385         }
386       }
387     }
388   }
389
390   if( !resourceReady )
391   {
392     DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: Resource ready trigger\n" );
393
394     mResourceReadyTrigger->Trigger();
395   }
396
397   auto timeToSleepUntil = currentFrameStartTime + std::chrono::nanoseconds( mFrameDurationNanoSeconds );
398
399 #if defined(DEBUG_ENABLED)
400   auto sleepDuration = std::chrono::duration_cast< std::chrono::milliseconds >( timeToSleepUntil - std::chrono::system_clock::now() );
401
402   DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "VectorRasterizeThread::Rasterize: [current = %d, sleep duration = %lld]\n", mCurrentFrame, sleepDuration.count() );
403 #endif
404
405   std::this_thread::sleep_until( timeToSleepUntil );
406 }
407
408 } // namespace Internal
409
410 } // namespace Toolkit
411
412 } // namespace Dali