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