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