Merge "Add ApplyCustomFragmentPrefix" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / image-loader / image-load-thread.cpp
1 /*
2  * Copyright (c) 2021 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 "image-load-thread.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/image-loading.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 namespace Dali
28 {
29 namespace Toolkit
30 {
31 namespace Internal
32 {
33 LoadingTask::LoadingTask(uint32_t id, Dali::AnimatedImageLoading animatedImageLoading, uint32_t frameIndex)
34 : pixelBuffer(),
35   url(),
36   id(id),
37   dimensions(),
38   fittingMode(),
39   samplingMode(),
40   orientationCorrection(),
41   preMultiplyOnLoad(DevelAsyncImageLoader::PreMultiplyOnLoad::OFF),
42   isMaskTask(false),
43   maskPixelBuffer(),
44   contentScale(1.0f),
45   cropToMask(false),
46   animatedImageLoading(animatedImageLoading),
47   frameIndex(frameIndex)
48 {
49 }
50
51 LoadingTask::LoadingTask(uint32_t id, const VisualUrl& url, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad)
52 : pixelBuffer(),
53   url(url),
54   id(id),
55   dimensions(dimensions),
56   fittingMode(fittingMode),
57   samplingMode(samplingMode),
58   orientationCorrection(orientationCorrection),
59   preMultiplyOnLoad(preMultiplyOnLoad),
60   isMaskTask(false),
61   maskPixelBuffer(),
62   contentScale(1.0f),
63   cropToMask(false),
64   animatedImageLoading(),
65   frameIndex(0u)
66 {
67 }
68
69 LoadingTask::LoadingTask(uint32_t id, Devel::PixelBuffer pixelBuffer, Devel::PixelBuffer maskPixelBuffer, float contentScale, bool cropToMask, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad)
70 : pixelBuffer(pixelBuffer),
71   url(""),
72   id(id),
73   dimensions(),
74   fittingMode(),
75   samplingMode(),
76   orientationCorrection(),
77   preMultiplyOnLoad(preMultiplyOnLoad),
78   isMaskTask(true),
79   maskPixelBuffer(maskPixelBuffer),
80   contentScale(contentScale),
81   cropToMask(cropToMask),
82   animatedImageLoading(),
83   frameIndex(0u)
84 {
85 }
86
87 void LoadingTask::Load()
88 {
89   if(animatedImageLoading)
90   {
91     pixelBuffer = animatedImageLoading.LoadFrame(frameIndex);
92   }
93   else if(url.IsLocalResource())
94   {
95     pixelBuffer = Dali::LoadImageFromFile(url.GetUrl(), dimensions, fittingMode, samplingMode, orientationCorrection);
96   }
97   else
98   {
99     pixelBuffer = Dali::DownloadImageSynchronously(url.GetUrl(), dimensions, fittingMode, samplingMode, orientationCorrection);
100   }
101
102   if(!pixelBuffer)
103   {
104     DALI_LOG_ERROR("LoadingTask::Load: Loading is failed: %s\n", url.GetUrl().c_str());
105   }
106 }
107
108 void LoadingTask::ApplyMask()
109 {
110   pixelBuffer.ApplyMask(maskPixelBuffer, contentScale, cropToMask);
111 }
112
113 void LoadingTask::MultiplyAlpha()
114 {
115   if(pixelBuffer && Pixel::HasAlpha(pixelBuffer.GetPixelFormat()))
116   {
117     if(preMultiplyOnLoad == DevelAsyncImageLoader::PreMultiplyOnLoad::ON)
118     {
119       pixelBuffer.MultiplyColorByAlpha();
120     }
121   }
122 }
123
124 ImageLoadThread::ImageLoadThread(EventThreadCallback* trigger)
125 : mTrigger(trigger),
126   mLogFactory(Dali::Adaptor::Get().GetLogFactory())
127 {
128 }
129
130 ImageLoadThread::~ImageLoadThread()
131 {
132   // add an empty task would stop the thread from conditional wait.
133   AddTask(NULL);
134   // stop the thread
135   Join();
136
137   delete mTrigger;
138
139   for(auto&& iter : mLoadQueue)
140   {
141     delete iter;
142   }
143   mLoadQueue.Clear();
144
145   for(auto&& iter : mCompleteQueue)
146   {
147     delete iter;
148   }
149   mCompleteQueue.Clear();
150 }
151
152 void ImageLoadThread::Run()
153 {
154   SetThreadName("ImageLoadThread");
155   mLogFactory.InstallLogFunction();
156
157   while(LoadingTask* task = NextTaskToProcess())
158   {
159     if(!task->isMaskTask)
160     {
161       task->Load();
162     }
163     else
164     {
165       task->ApplyMask();
166     }
167     task->MultiplyAlpha();
168
169     AddCompletedTask(task);
170   }
171 }
172
173 void ImageLoadThread::AddTask(LoadingTask* task)
174 {
175   bool wasEmpty = false;
176   {
177     // Lock while adding task to the queue
178     ConditionalWait::ScopedLock lock(mConditionalWait);
179     wasEmpty = mLoadQueue.Empty();
180     mLoadQueue.PushBack(task);
181   }
182
183   if(wasEmpty)
184   {
185     // wake up the image loading thread
186     mConditionalWait.Notify();
187   }
188 }
189
190 LoadingTask* ImageLoadThread::NextCompletedTask()
191 {
192   // Lock while popping task out from the queue
193   Mutex::ScopedLock lock(mMutex);
194
195   if(mCompleteQueue.Empty())
196   {
197     return NULL;
198   }
199
200   Vector<LoadingTask*>::Iterator next     = mCompleteQueue.Begin();
201   LoadingTask*                   nextTask = *next;
202   mCompleteQueue.Erase(next);
203
204   return nextTask;
205 }
206
207 bool ImageLoadThread::CancelTask(uint32_t loadingTaskId)
208 {
209   // Lock while remove task from the queue
210   ConditionalWait::ScopedLock lock(mConditionalWait);
211
212   for(Vector<LoadingTask*>::Iterator iter = mLoadQueue.Begin(); iter != mLoadQueue.End(); ++iter)
213   {
214     if((*iter)->id == loadingTaskId)
215     {
216       delete(*iter);
217       mLoadQueue.Erase(iter);
218       return true;
219     }
220   }
221
222   return false;
223 }
224
225 void ImageLoadThread::CancelAll()
226 {
227   // Lock while remove task from the queue
228   ConditionalWait::ScopedLock lock(mConditionalWait);
229
230   for(Vector<LoadingTask*>::Iterator iter = mLoadQueue.Begin(); iter != mLoadQueue.End(); ++iter)
231   {
232     delete(*iter);
233   }
234   mLoadQueue.Clear();
235 }
236
237 LoadingTask* ImageLoadThread::NextTaskToProcess()
238 {
239   // Lock while popping task out from the queue
240   ConditionalWait::ScopedLock lock(mConditionalWait);
241
242   while(mLoadQueue.Empty())
243   {
244     mConditionalWait.Wait(lock);
245   }
246
247   Vector<LoadingTask*>::Iterator next     = mLoadQueue.Begin();
248   LoadingTask*                   nextTask = *next;
249   mLoadQueue.Erase(next);
250
251   return nextTask;
252 }
253
254 void ImageLoadThread::AddCompletedTask(LoadingTask* task)
255 {
256   // Lock while adding task to the queue
257   Mutex::ScopedLock lock(mMutex);
258   mCompleteQueue.PushBack(task);
259
260   // wake up the main thread
261   mTrigger->Trigger();
262 }
263
264 } // namespace Internal
265
266 } // namespace Toolkit
267
268 } // namespace Dali