Merge "Support Asynchronous Loading of Animated Image" into 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
145 void ImageLoadThread::Run()
146 {
147   SetThreadName( "ImageLoadThread" );
148   mLogFactory.InstallLogFunction();
149
150   while( LoadingTask* task = NextTaskToProcess() )
151   {
152     if( !task->isMaskTask )
153     {
154       task->Load();
155     }
156     else
157     {
158       task->ApplyMask();
159     }
160     task->MultiplyAlpha();
161
162     AddCompletedTask( task );
163   }
164 }
165
166 void ImageLoadThread::AddTask( LoadingTask* task )
167 {
168   bool wasEmpty = false;
169   {
170     // Lock while adding task to the queue
171     ConditionalWait::ScopedLock lock( mConditionalWait );
172     wasEmpty = mLoadQueue.Empty();
173     mLoadQueue.PushBack( task );
174   }
175
176   if( wasEmpty )
177   {
178     // wake up the image loading thread
179     mConditionalWait.Notify();
180   }
181 }
182
183 LoadingTask* ImageLoadThread::NextCompletedTask()
184 {
185   // Lock while popping task out from the queue
186   Mutex::ScopedLock lock( mMutex );
187
188   if( mCompleteQueue.Empty() )
189   {
190     return NULL;
191   }
192
193   Vector< LoadingTask* >::Iterator next = mCompleteQueue.Begin();
194   LoadingTask* nextTask = *next;
195   mCompleteQueue.Erase( next );
196
197   return nextTask;
198 }
199
200 bool ImageLoadThread::CancelTask( uint32_t loadingTaskId )
201 {
202   // Lock while remove task from the queue
203   ConditionalWait::ScopedLock lock( mConditionalWait );
204
205   for( Vector< LoadingTask* >::Iterator iter = mLoadQueue.Begin(); iter != mLoadQueue.End(); ++iter )
206   {
207     if( (*iter)->id == loadingTaskId )
208     {
209       delete (*iter);
210       mLoadQueue.Erase( iter );
211       return true;
212     }
213   }
214
215   return false;
216 }
217
218
219 void ImageLoadThread::CancelAll()
220 {
221   // Lock while remove task from the queue
222   ConditionalWait::ScopedLock lock( mConditionalWait );
223
224   for( Vector< LoadingTask* >::Iterator iter = mLoadQueue.Begin(); iter != mLoadQueue.End(); ++iter )
225   {
226     delete ( *iter );
227   }
228   mLoadQueue.Clear();
229 }
230
231 LoadingTask* ImageLoadThread::NextTaskToProcess()
232 {
233   // Lock while popping task out from the queue
234   ConditionalWait::ScopedLock lock( mConditionalWait );
235
236   while( mLoadQueue.Empty() )
237   {
238     mConditionalWait.Wait( lock );
239   }
240
241   Vector< LoadingTask* >::Iterator next = mLoadQueue.Begin();
242   LoadingTask* nextTask = *next;
243   mLoadQueue.Erase( next );
244
245   return nextTask;
246 }
247
248 void ImageLoadThread::AddCompletedTask( LoadingTask* task )
249 {
250   // Lock while adding task to the queue
251   Mutex::ScopedLock lock( mMutex );
252   mCompleteQueue.PushBack( task );
253
254   // wake up the main thread
255   mTrigger->Trigger();
256 }
257
258 } // namespace Internal
259
260 } // namespace Toolkit
261
262 } // namespace Dali