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