Merge "Updating test-suite to match core" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / svg / svg-rasterize-thread.cpp
1 /*
2  * Copyright (c) 2016 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 "svg-rasterize-thread.h"
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/third-party/nanosvg/nanosvgrast.h>
23 #include <dali-toolkit/internal/visuals/svg/svg-visual.h>
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Internal
32 {
33
34 RasterizingTask::RasterizingTask( SvgVisual* svgRenderer, NSVGimage* parsedSvg, unsigned int width, unsigned int height )
35 : mSvgVisual( svgRenderer ),
36   mParsedSvg( parsedSvg ),
37   mWidth( width ),
38   mHeight( height )
39 {
40 }
41
42 void RasterizingTask::Rasterize( NSVGrasterizer* rasterizer )
43 {
44   if( mWidth > 0u && mHeight > 0u )
45   {
46     float scaleX =  static_cast<float>( mWidth ) /  mParsedSvg->width;
47     float scaleY =  static_cast<float>( mHeight ) /  mParsedSvg->height;
48     float scale = scaleX < scaleY ? scaleX : scaleY;
49     unsigned int bufferStride = mWidth*Pixel::GetBytesPerPixel( Pixel::RGBA8888 );
50     unsigned int bufferSize = bufferStride * mHeight;
51
52     unsigned char* buffer = new unsigned char [bufferSize];
53     nsvgRasterize(rasterizer, mParsedSvg, 0.f,0.f,scale,
54         buffer, mWidth, mHeight,
55         bufferStride );
56
57     mPixelData = Dali::PixelData::New( buffer, bufferSize, mWidth, mHeight, Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY );
58   }
59 }
60
61 SvgVisual* RasterizingTask::GetSvgVisual() const
62 {
63   return mSvgVisual.Get();
64 }
65
66 PixelData RasterizingTask::GetPixelData() const
67 {
68   return mPixelData;
69 }
70
71 SvgRasterizeThread::SvgRasterizeThread( EventThreadCallback* trigger )
72 : mTrigger( trigger ),
73   mIsThreadWaiting( false )
74 {
75   mRasterizer = nsvgCreateRasterizer();
76 }
77
78 SvgRasterizeThread::~SvgRasterizeThread()
79 {
80
81   nsvgDeleteRasterizer( mRasterizer );
82   delete mTrigger;
83 }
84
85 void SvgRasterizeThread::TerminateThread( SvgRasterizeThread*& thread )
86 {
87   if( thread )
88   {
89     // add an empty task would stop the thread from conditional wait.
90     thread->AddTask( RasterizingTaskPtr() );
91     // stop the thread
92     thread->Join();
93     // delete the thread
94     delete thread;
95     thread = NULL;
96   }
97 }
98
99 void SvgRasterizeThread::AddTask( RasterizingTaskPtr task )
100 {
101   bool wasEmpty = false;
102
103   {
104     // Lock while adding task to the queue
105     ConditionalWait::ScopedLock lock( mConditionalWait );
106     wasEmpty = mRasterizeTasks.empty();
107     if( !wasEmpty && task != NULL)
108     {
109       // Remove the tasks with the same renderer.
110       // Older task which waiting to rasterize and apply the svg to the same renderer is expired.
111       for( std::vector< RasterizingTaskPtr >::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it )
112       {
113         if( (*it) && (*it)->GetSvgVisual() == task->GetSvgVisual() )
114         {
115           mRasterizeTasks.erase( it );
116           break;
117         }
118       }
119     }
120     mRasterizeTasks.push_back( task );
121   }
122
123   if( wasEmpty)
124   {
125     // wake up the image loading thread
126     mConditionalWait.Notify();
127   }
128 }
129
130 RasterizingTaskPtr SvgRasterizeThread::NextCompletedTask()
131 {
132   // Lock while popping task out from the queue
133   Mutex::ScopedLock lock( mMutex );
134
135   if( mCompletedTasks.empty() )
136   {
137     return RasterizingTaskPtr();
138   }
139
140   std::vector< RasterizingTaskPtr >::iterator next = mCompletedTasks.begin();
141   RasterizingTaskPtr nextTask = *next;
142   mCompletedTasks.erase( next );
143
144   return nextTask;
145 }
146
147 void SvgRasterizeThread::RemoveTask( SvgVisual* renderer )
148 {
149   // Lock while remove task from the queue
150   ConditionalWait::ScopedLock lock( mConditionalWait );
151   if( !mRasterizeTasks.empty() )
152   {
153     for( std::vector< RasterizingTaskPtr >::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it )
154     {
155       if( (*it) &&  (*it)->GetSvgVisual() == renderer )
156       {
157         mRasterizeTasks.erase( it );
158         break;
159       }
160     }
161   }
162 }
163
164 void SvgRasterizeThread::DeleteImage( NSVGimage* parsedSvg )
165 {
166   // Lock while adding image to the delete queue
167   ConditionalWait::ScopedLock lock( mConditionalWait );
168
169   if( mIsThreadWaiting ) // no rasterization is ongoing, save to delete
170   {
171     nsvgDelete( parsedSvg );
172   }
173   else // wait to delete until current rasterization completed.
174   {
175     mDeleteSvg.PushBack( parsedSvg );
176   }
177 }
178
179 RasterizingTaskPtr SvgRasterizeThread::NextTaskToProcess()
180 {
181   // Lock while popping task out from the queue
182   ConditionalWait::ScopedLock lock( mConditionalWait );
183
184   // Delete the image here to make sure that it is not used in the nsvgRasterize()
185   if( !mDeleteSvg.Empty() )
186   {
187     for( Vector< NSVGimage* >::Iterator it = mDeleteSvg.Begin(), endIt = mDeleteSvg.End();
188         it != endIt;
189         ++it )
190     {
191       nsvgDelete( *it );
192     }
193     mDeleteSvg.Clear();
194   }
195
196   // conditional wait
197   while( mRasterizeTasks.empty() )
198   {
199     mIsThreadWaiting = true;
200     mConditionalWait.Wait( lock );
201   }
202   mIsThreadWaiting = false;
203
204   // pop out the next task from the queue
205   std::vector< RasterizingTaskPtr >::iterator next = mRasterizeTasks.begin();
206   RasterizingTaskPtr nextTask = *next;
207   mRasterizeTasks.erase( next );
208
209   return nextTask;
210 }
211
212 void SvgRasterizeThread::AddCompletedTask( RasterizingTaskPtr task )
213 {
214   // Lock while adding task to the queue
215   Mutex::ScopedLock lock( mMutex );
216   mCompletedTasks.push_back( task );
217
218   // wake up the main thread
219   mTrigger->Trigger();
220 }
221
222 void SvgRasterizeThread::Run()
223 {
224   while( RasterizingTaskPtr task = NextTaskToProcess() )
225   {
226     task->Rasterize( mRasterizer );
227     AddCompletedTask( task );
228   }
229 }
230
231 } // namespace Internal
232
233 } // namespace Toolkit
234
235 } // namespace Dali