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