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