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