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