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