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