Show the broken image in case of invalid svg files
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / svg / svg-rasterize-thread.cpp
1 /*
2  * Copyright (c) 2020 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/adaptor-framework/adaptor.h>
25 #include <dali/integration-api/debug.h>
26
27 // INTERNAL INCLUDES
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 RasterizingTask::RasterizingTask(SvgVisual* svgRenderer, VectorImageRenderer vectorRenderer, const VisualUrl& url, float dpi, unsigned int width, unsigned int height)
39 : mSvgVisual(svgRenderer),
40   mVectorRenderer(vectorRenderer),
41   mUrl(url),
42   mDpi(dpi),
43   mWidth(width),
44   mHeight(height),
45   mLoadSuccess(false)
46 {
47
48 }
49
50 RasterizingTask::~RasterizingTask()
51 {
52 }
53
54 void RasterizingTask::Load()
55 {
56   if(!mUrl.IsLocalResource())
57   {
58     Dali::Vector<uint8_t> remoteBuffer;
59     if(!Dali::FileLoader::DownloadFileSynchronously(mUrl.GetUrl(), remoteBuffer))
60     {
61       DALI_LOG_ERROR("RasterizingTask::Load: Failed to download file! [%s]\n", mUrl.GetUrl().c_str());
62       return;
63     }
64
65     remoteBuffer.PushBack('\0');
66
67     if(!mVectorRenderer.Load(remoteBuffer, mDpi))
68     {
69       DALI_LOG_ERROR("RasterizingTask::Load:Failed to load data! [%s]\n", mUrl.GetUrl().c_str());
70       return;
71     }
72
73     mLoadSuccess = true;
74   }
75   else
76   {
77     mLoadSuccess = true;
78   }
79 }
80
81 void RasterizingTask::Rasterize()
82 {
83   if(mWidth <= 0u || mHeight <= 0u)
84   {
85     DALI_LOG_ERROR("RasterizingTask::Rasterize: Size is zero!\n");
86     return;
87   }
88
89   Devel::PixelBuffer pixelBuffer = Devel::PixelBuffer::New(mWidth, mHeight, Dali::Pixel::RGBA8888);
90
91   uint32_t defaultWidth, defaultHeight;
92   mVectorRenderer.GetDefaultSize(defaultWidth, defaultHeight);
93
94   float scaleX = static_cast<float>(mWidth) / static_cast<float>(defaultWidth);
95   float scaleY = static_cast<float>(mHeight) / static_cast<float>(defaultHeight);
96   float scale  = scaleX < scaleY ? scaleX : scaleY;
97
98   if(!mVectorRenderer.Rasterize(pixelBuffer, scale))
99   {
100     DALI_LOG_ERROR("RasterizingTask::Rasterize: Rasterize is failed! [%s]\n", mUrl.GetUrl().c_str());
101     return;
102   }
103
104   mPixelData = Devel::PixelBuffer::Convert(pixelBuffer);
105 }
106
107 VectorImageRenderer RasterizingTask::GetVectorRenderer() const
108 {
109   return mVectorRenderer;
110 }
111
112 bool RasterizingTask::IsLoaded() const
113 {
114   return mLoadSuccess;
115 }
116
117 SvgVisual* RasterizingTask::GetSvgVisual() const
118 {
119   return mSvgVisual.Get();
120 }
121
122 PixelData RasterizingTask::GetPixelData() const
123 {
124   return mPixelData;
125 }
126
127 SvgRasterizeThread::SvgRasterizeThread()
128 : mTrigger( new EventThreadCallback( MakeCallback( this, &SvgRasterizeThread::ApplyRasterizedSVGToSampler ) ) ),
129   mLogFactory( Dali::Adaptor::Get().GetLogFactory() ),
130   mIsThreadWaiting( false ),
131   mProcessorRegistered( false )
132 {
133 }
134
135 SvgRasterizeThread::~SvgRasterizeThread()
136 {
137   if( mProcessorRegistered )
138   {
139     Adaptor::Get().UnregisterProcessor( *this );
140   }
141 }
142
143 void SvgRasterizeThread::TerminateThread( SvgRasterizeThread*& thread )
144 {
145   if( thread )
146   {
147     // add an empty task would stop the thread from conditional wait.
148     thread->AddTask( RasterizingTaskPtr() );
149     // stop the thread
150     thread->Join();
151     // delete the thread
152     delete thread;
153     thread = NULL;
154   }
155 }
156
157 void SvgRasterizeThread::AddTask( RasterizingTaskPtr task )
158 {
159   bool wasEmpty = false;
160
161   {
162     // Lock while adding task to the queue
163     ConditionalWait::ScopedLock lock( mConditionalWait );
164     wasEmpty = mRasterizeTasks.empty();
165     if( !wasEmpty && task != NULL)
166     {
167       // Remove the tasks with the same renderer.
168       // Older task which waiting to rasterize and apply the svg to the same renderer is expired.
169       for( std::vector< RasterizingTaskPtr >::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it )
170       {
171         if( (*it) && (*it)->GetSvgVisual() == task->GetSvgVisual() )
172         {
173           mRasterizeTasks.erase( it );
174           break;
175         }
176       }
177     }
178     mRasterizeTasks.push_back( task );
179
180     if( !mProcessorRegistered )
181     {
182       Adaptor::Get().RegisterProcessor( *this );
183       mProcessorRegistered = true;
184     }
185   }
186
187   if( wasEmpty)
188   {
189     // wake up the image loading thread
190     mConditionalWait.Notify();
191   }
192 }
193
194 RasterizingTaskPtr SvgRasterizeThread::NextCompletedTask()
195 {
196   // Lock while popping task out from the queue
197   Mutex::ScopedLock lock( mMutex );
198
199   if( mCompletedTasks.empty() )
200   {
201     return RasterizingTaskPtr();
202   }
203
204   std::vector< RasterizingTaskPtr >::iterator next = mCompletedTasks.begin();
205   RasterizingTaskPtr nextTask = *next;
206   mCompletedTasks.erase( next );
207
208   return nextTask;
209 }
210
211 void SvgRasterizeThread::RemoveTask( SvgVisual* visual )
212 {
213   // Lock while remove task from the queue
214   ConditionalWait::ScopedLock lock( mConditionalWait );
215   if( !mRasterizeTasks.empty() )
216   {
217     for( std::vector< RasterizingTaskPtr >::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it )
218     {
219       if( (*it) &&  (*it)->GetSvgVisual() == visual )
220       {
221         mRasterizeTasks.erase( it );
222         break;
223       }
224     }
225   }
226
227   UnregisterProcessor();
228 }
229
230 void SvgRasterizeThread::DeleteImage( VectorImageRenderer vectorRenderer )
231 {
232   // Lock while adding image to the delete queue
233   ConditionalWait::ScopedLock lock( mConditionalWait );
234
235   if( mIsThreadWaiting ) // no rasterization is ongoing, save to delete
236   {
237     // TODO: what?
238   }
239   else // wait to delete until current rasterization completed.
240   {
241     mDeleteSvg.PushBack( &vectorRenderer );
242   }
243 }
244
245 RasterizingTaskPtr SvgRasterizeThread::NextTaskToProcess()
246 {
247   // Lock while popping task out from the queue
248   ConditionalWait::ScopedLock lock( mConditionalWait );
249
250   // Delete the image here to make sure that it is not used in the nsvgRasterize()
251   if( !mDeleteSvg.Empty() )
252   {
253     mDeleteSvg.Clear();
254   }
255
256   // conditional wait
257   while( mRasterizeTasks.empty() )
258   {
259     mIsThreadWaiting = true;
260     mConditionalWait.Wait( lock );
261   }
262   mIsThreadWaiting = false;
263
264   // pop out the next task from the queue
265   std::vector< RasterizingTaskPtr >::iterator next = mRasterizeTasks.begin();
266   RasterizingTaskPtr nextTask = *next;
267   mRasterizeTasks.erase( next );
268
269   return nextTask;
270 }
271
272 void SvgRasterizeThread::AddCompletedTask( RasterizingTaskPtr task )
273 {
274   // Lock while adding task to the queue
275   Mutex::ScopedLock lock( mMutex );
276   mCompletedTasks.push_back( task );
277
278   // wake up the main thread
279   mTrigger->Trigger();
280 }
281
282 void SvgRasterizeThread::Run()
283 {
284   SetThreadName( "SVGThread" );
285   mLogFactory.InstallLogFunction();
286
287   while( RasterizingTaskPtr task = NextTaskToProcess() )
288   {
289     task->Load( );
290     task->Rasterize( );
291     AddCompletedTask( task );
292   }
293 }
294
295 void SvgRasterizeThread::ApplyRasterizedSVGToSampler()
296 {
297   while( RasterizingTaskPtr task = NextCompletedTask() )
298   {
299     task->GetSvgVisual()->ApplyRasterizedImage(task->GetVectorRenderer(), task->GetPixelData(), task->IsLoaded());
300   }
301
302   UnregisterProcessor();
303 }
304
305 void SvgRasterizeThread::Process()
306 {
307   ApplyRasterizedSVGToSampler();
308 }
309
310 void SvgRasterizeThread::UnregisterProcessor()
311 {
312   if ( mProcessorRegistered )
313   {
314     if( mRasterizeTasks.empty() && mCompletedTasks.empty() )
315     {
316       Adaptor::Get().UnregisterProcessor( *this );
317       mProcessorRegistered = false;
318     }
319   }
320 }
321
322
323 } // namespace Internal
324
325 } // namespace Toolkit
326
327 } // namespace Dali