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