Image class Usage Removal
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / texture-manager-impl.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 <dali-toolkit/internal/visuals/texture-manager-impl.h>
20
21 // EXTERNAL HEADERS
22 #include <cstdlib>
23 #include <string>
24 #include <dali/public-api/math/vector4.h>
25 #include <dali/devel-api/adaptor-framework/environment-variable.h>
26 #include <dali/devel-api/adaptor-framework/image-loading.h>
27 #include <dali/devel-api/common/hash.h>
28 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
29 #include <dali/integration-api/debug.h>
30
31 // INTERNAL HEADERS
32 #include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
33 #include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
34 #include <dali-toolkit/internal/visuals/image-atlas-manager.h>
35
36 namespace
37 {
38
39 constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u};
40 constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u};
41
42 constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS";
43 constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS";
44
45 size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue)
46 {
47   using Dali::EnvironmentVariable::GetEnvironmentVariable;
48   auto numberString = GetEnvironmentVariable(environmentVariable);
49   auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0;
50   constexpr auto MAX_NUMBER_OF_THREADS = 100u;
51   DALI_ASSERT_DEBUG( numberOfThreads < MAX_NUMBER_OF_THREADS );
52   return ( numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS ) ? numberOfThreads : defaultValue;
53 }
54
55 size_t GetNumberOfLocalLoaderThreads()
56 {
57   return GetNumberOfThreads(NUMBER_OF_LOCAL_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS);
58 }
59
60 size_t GetNumberOfRemoteLoaderThreads()
61 {
62   return GetNumberOfThreads(NUMBER_OF_REMOTE_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS);
63 }
64
65 } // namespace
66
67 namespace Dali
68 {
69
70 namespace Toolkit
71 {
72
73 namespace Internal
74 {
75
76 namespace
77 {
78
79 #ifdef DEBUG_ENABLED
80 Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_TEXTURE_MANAGER" );
81
82 #define GET_LOAD_STATE_STRING( loadState ) \
83   loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :             \
84     loadState == TextureManager::LOADING ? "LOADING" :                   \
85     loadState == TextureManager::LOAD_FINISHED ? "LOAD_FINISHED" :       \
86     loadState == TextureManager::WAITING_FOR_MASK ? "WAITING_FOR_MASK" : \
87     loadState == TextureManager::MASK_APPLYING ? "MASK_APPLYING" :         \
88     loadState == TextureManager::MASK_APPLIED ? "MASK_APPLIED" :         \
89     loadState == TextureManager::UPLOADED ? "UPLOADED" :                 \
90     loadState == TextureManager::CANCELLED ? "CANCELLED" :               \
91     loadState == TextureManager::LOAD_FAILED ? "LOAD_FAILED" : "Unknown"
92
93 #endif
94
95 const uint32_t      DEFAULT_ATLAS_SIZE( 1024u );                     ///< This size can fit 8 by 8 images of average size 128 * 128
96 const Vector4       FULL_ATLAS_RECT( 0.0f, 0.0f, 1.0f, 1.0f );       ///< UV Rectangle that covers the full Texture
97 const int           INVALID_INDEX( -1 );                             ///< Invalid index used to represent a non-existant TextureInfo struct
98 const int           INVALID_CACHE_INDEX( -1 ); ///< Invalid Cache index
99
100
101 void PreMultiply( Devel::PixelBuffer pixelBuffer, TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
102 {
103   if( Pixel::HasAlpha( pixelBuffer.GetPixelFormat() ) )
104   {
105     if( preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD )
106     {
107       pixelBuffer.MultiplyColorByAlpha();
108     }
109   }
110   else
111   {
112     preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
113   }
114 }
115
116 } // Anonymous namespace
117
118 TextureManager::MaskingData::MaskingData()
119 : mAlphaMaskUrl(),
120   mAlphaMaskId( INVALID_TEXTURE_ID ),
121   mContentScaleFactor( 1.0f ),
122   mCropToMask( true )
123 {
124 }
125
126 TextureManager::TextureManager()
127 : mAsyncLocalLoaders( GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ),
128   mAsyncRemoteLoaders( GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ),
129   mExternalTextures(),
130   mLifecycleObservers(),
131   mLoadQueue(),
132   mBrokenImageUrl(""),
133   mCurrentTextureId( 0 ),
134   mQueueLoadFlag(false)
135 {
136 }
137
138 TextureManager::~TextureManager()
139 {
140   for( auto iter = mLifecycleObservers.Begin(), endIter = mLifecycleObservers.End(); iter != endIter; ++iter)
141   {
142     (*iter)->TextureManagerDestroyed();
143   }
144 }
145
146 Devel::PixelBuffer TextureManager::LoadPixelBuffer(
147   const VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode, bool synchronousLoading, TextureUploadObserver* textureObserver, bool orientationCorrection, TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
148 {
149   Devel::PixelBuffer pixelBuffer;
150   if( synchronousLoading )
151   {
152     if( url.IsValid() )
153     {
154       pixelBuffer = LoadImageFromFile( url.GetUrl(), desiredSize, fittingMode, samplingMode,
155                                        orientationCorrection  );
156       if( pixelBuffer && preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD )
157       {
158         PreMultiply( pixelBuffer, preMultiplyOnLoad );
159       }
160     }
161   }
162   else
163   {
164     RequestLoadInternal( url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS,
165                          false, RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED,
166                          preMultiplyOnLoad );
167   }
168
169   return pixelBuffer;
170 }
171
172 TextureSet TextureManager::LoadTexture(
173   const VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode,
174   Dali::SamplingMode::Type samplingMode, MaskingDataPointer& maskInfo,
175   bool synchronousLoading, TextureManager::TextureId& textureId, Vector4& textureRect,
176   Dali::ImageDimensions& textureRectSize, bool& atlasingStatus, bool& loadingStatus,
177   Dali::WrapMode::Type wrapModeU, Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver,
178   AtlasUploadObserver* atlasObserver, ImageAtlasManagerPtr imageAtlasManager, bool orientationCorrection,
179   TextureManager::ReloadPolicy reloadPolicy, TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
180 {
181   TextureSet textureSet;
182
183   loadingStatus = false;
184   textureRect = FULL_ATLAS_RECT;
185
186   if( VisualUrl::TEXTURE == url.GetProtocolType())
187   {
188     std::string location = url.GetLocation();
189     if( location.size() > 0u )
190     {
191       TextureId id = std::stoi( location );
192       for( auto&& elem : mExternalTextures )
193       {
194         if( elem.textureId == id )
195         {
196           preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
197           textureId = elem.textureId;
198           return elem.textureSet;
199         }
200       }
201     }
202   }
203   else if( synchronousLoading )
204   {
205     PixelData data;
206     if( url.IsValid() )
207     {
208       Devel::PixelBuffer pixelBuffer = LoadImageFromFile( url.GetUrl(), desiredSize, fittingMode, samplingMode,
209                                        orientationCorrection  );
210       if( maskInfo )
211       {
212         Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile( maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(),
213                                              FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true  );
214         if( maskPixelBuffer )
215         {
216           pixelBuffer.ApplyMask( maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask );
217         }
218       }
219       if( pixelBuffer )
220       {
221         PreMultiply( pixelBuffer, preMultiplyOnLoad );
222         data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
223       }
224     }
225     if( !data )
226     {
227       // use broken image
228       Devel::PixelBuffer pixelBuffer = LoadImageFromFile( mBrokenImageUrl );
229       if( pixelBuffer )
230       {
231         PreMultiply( pixelBuffer, preMultiplyOnLoad );
232         data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
233       }
234       Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(),
235                                       data.GetWidth(), data.GetHeight() );
236       texture.Upload( data );
237       textureSet = TextureSet::New();
238       textureSet.SetTexture( 0u, texture );
239     }
240     else
241     {
242       if( atlasingStatus ) // attempt atlasing
243       {
244         textureSet = imageAtlasManager->Add( textureRect, data );
245       }
246       if( !textureSet ) // big image, no atlasing or atlasing failed
247       {
248         atlasingStatus = false;
249         Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(),
250                                         data.GetWidth(), data.GetHeight() );
251         texture.Upload( data );
252         textureSet = TextureSet::New();
253         textureSet.SetTexture( 0u, texture );
254       }
255       else
256       {
257         textureRectSize.SetWidth(data.GetWidth());
258         textureRectSize.SetHeight(data.GetHeight());
259       }
260     }
261   }
262   else
263   {
264     loadingStatus = true;
265     if( atlasingStatus )
266     {
267       textureSet = imageAtlasManager->Add( textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver);
268     }
269     if( !textureSet ) // big image, no atlasing or atlasing failed
270     {
271       atlasingStatus = false;
272       if( !maskInfo )
273       {
274         textureId = RequestLoad( url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS,
275                                  textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad );
276       }
277       else
278       {
279         maskInfo->mAlphaMaskId = RequestMaskLoad( maskInfo->mAlphaMaskUrl );
280         textureId = RequestLoad( url,
281                                  maskInfo->mAlphaMaskId,
282                                  maskInfo->mContentScaleFactor,
283                                  desiredSize,
284                                  fittingMode, samplingMode,
285                                  TextureManager::NO_ATLAS,
286                                  maskInfo->mCropToMask,
287                                  textureObserver,
288                                  orientationCorrection,
289                                  reloadPolicy, preMultiplyOnLoad );
290       }
291
292       TextureManager::LoadState loadState = GetTextureStateInternal( textureId );
293       if( loadState == TextureManager::UPLOADED )
294       {
295         // UploadComplete has already been called - keep the same texture set
296         textureSet = GetTextureSet( textureId );
297       }
298
299       // If we are loading the texture, or waiting for the ready signal handler to complete, inform
300       // caller that they need to wait.
301       loadingStatus = ( loadState == TextureManager::LOADING ||
302                         loadState == TextureManager::WAITING_FOR_MASK ||
303                         loadState == TextureManager::MASK_APPLYING ||
304                         loadState == TextureManager::MASK_APPLIED ||
305                         loadState == TextureManager::NOT_STARTED ||
306                         mQueueLoadFlag );
307
308     }
309     else
310     {
311       textureRectSize = desiredSize;
312     }
313   }
314
315   if( ! atlasingStatus && textureSet )
316   {
317     Sampler sampler = Sampler::New();
318     sampler.SetWrapMode(  wrapModeU, wrapModeV  );
319     textureSet.SetSampler( 0u, sampler );
320   }
321
322   return textureSet;
323 }
324
325 TextureManager::TextureId TextureManager::RequestLoad(
326   const VisualUrl&                url,
327   const ImageDimensions           desiredSize,
328   FittingMode::Type               fittingMode,
329   Dali::SamplingMode::Type        samplingMode,
330   const UseAtlas                  useAtlas,
331   TextureUploadObserver*          observer,
332   bool                            orientationCorrection,
333   TextureManager::ReloadPolicy    reloadPolicy,
334   TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
335 {
336   return RequestLoadInternal( url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas,
337                               false, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy,
338                               preMultiplyOnLoad );
339 }
340
341 TextureManager::TextureId TextureManager::RequestLoad(
342   const VisualUrl&                url,
343   TextureId                       maskTextureId,
344   float                           contentScale,
345   const ImageDimensions           desiredSize,
346   FittingMode::Type               fittingMode,
347   Dali::SamplingMode::Type        samplingMode,
348   const UseAtlas                  useAtlas,
349   bool                            cropToMask,
350   TextureUploadObserver*          observer,
351   bool                            orientationCorrection,
352   TextureManager::ReloadPolicy    reloadPolicy,
353   TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
354 {
355   return RequestLoadInternal( url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas,
356                               cropToMask, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy,
357                               preMultiplyOnLoad );
358 }
359
360 TextureManager::TextureId TextureManager::RequestMaskLoad( const VisualUrl& maskUrl )
361 {
362   // Use the normal load procedure to get the alpha mask.
363   auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
364   return RequestLoadInternal( maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL,
365                               SamplingMode::NO_FILTER, NO_ATLAS, false, KEEP_PIXEL_BUFFER, NULL, true,
366                               TextureManager::ReloadPolicy::CACHED, preMultiply );
367 }
368
369 TextureManager::TextureId TextureManager::RequestLoadInternal(
370   const VisualUrl&                url,
371   TextureId                       maskTextureId,
372   float                           contentScale,
373   const ImageDimensions           desiredSize,
374   FittingMode::Type               fittingMode,
375   Dali::SamplingMode::Type        samplingMode,
376   UseAtlas                        useAtlas,
377   bool                            cropToMask,
378   StorageType                     storageType,
379   TextureUploadObserver*          observer,
380   bool                            orientationCorrection,
381   TextureManager::ReloadPolicy    reloadPolicy,
382   TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
383 {
384   // First check if the requested Texture is cached.
385   const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas,
386                                                 maskTextureId );
387
388   TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
389   // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
390   int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas,
391                                       maskTextureId, preMultiplyOnLoad, storageType );
392
393   // Check if the requested Texture exists in the cache.
394   if( cacheIndex != INVALID_CACHE_INDEX )
395   {
396     if ( TextureManager::ReloadPolicy::CACHED == reloadPolicy )
397     {
398       // Mark this texture being used by another client resource. Forced reload would replace the current texture
399       // without the need for incrementing the reference count.
400       ++( mTextureInfoContainer[ cacheIndex ].referenceCount );
401     }
402     textureId = mTextureInfoContainer[ cacheIndex ].textureId;
403
404     // Update preMultiplyOnLoad value. It should be changed according to preMultiplied value of the cached info.
405     preMultiplyOnLoad = mTextureInfoContainer[ cacheIndex ].preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
406
407     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d\n",
408                    url.GetUrl().c_str(), observer, cacheIndex, textureId );
409   }
410
411   if( textureId == INVALID_TEXTURE_ID ) // There was no caching, or caching not required
412   {
413     // We need a new Texture.
414     textureId = GenerateUniqueTextureId();
415     bool preMultiply = ( preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD );
416     mTextureInfoContainer.push_back( TextureInfo( textureId, maskTextureId, url.GetUrl(),
417                                                   desiredSize, contentScale, fittingMode, samplingMode,
418                                                   false, cropToMask, useAtlas, textureHash, orientationCorrection,
419                                                   preMultiply ) );
420     cacheIndex = mTextureInfoContainer.size() - 1u;
421
422     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n",
423                    url.GetUrl().c_str(), observer, cacheIndex, textureId );
424   }
425
426   // The below code path is common whether we are using the cache or not.
427   // The textureInfoIndex now refers to either a pre-existing cached TextureInfo,
428   // or a new TextureInfo just created.
429   TextureInfo& textureInfo( mTextureInfoContainer[ cacheIndex ] );
430   textureInfo.maskTextureId = maskTextureId;
431   textureInfo.storageType = storageType;
432   textureInfo.orientationCorrection = orientationCorrection;
433
434   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::General, "TextureInfo loadState:%s\n",
435                  GET_LOAD_STATE_STRING(textureInfo.loadState ) );
436
437   // Force reloading of texture by setting loadState unless already loading or cancelled.
438   if ( TextureManager::ReloadPolicy::FORCED == reloadPolicy &&
439        TextureManager::LOADING != textureInfo.loadState &&
440        TextureManager::WAITING_FOR_MASK != textureInfo.loadState &&
441        TextureManager::MASK_APPLYING != textureInfo.loadState &&
442        TextureManager::MASK_APPLIED != textureInfo.loadState &&
443        TextureManager::CANCELLED != textureInfo.loadState )
444   {
445     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d\n",
446                    url.GetUrl().c_str(), observer, cacheIndex, textureId );
447
448     textureInfo.loadState = TextureManager::NOT_STARTED;
449   }
450
451   // Check if we should add the observer.
452   // Only do this if we have not loaded yet and it will not have loaded by the end of this method.
453   switch( textureInfo.loadState )
454   {
455     case TextureManager::LOAD_FAILED: // Failed notifies observer which then stops observing.
456     case TextureManager::NOT_STARTED:
457     {
458       LoadOrQueueTexture( textureInfo, observer ); // If called inside NotifyObservers, queues until afterwards
459       break;
460     }
461     case TextureManager::LOADING:
462     case TextureManager::WAITING_FOR_MASK:
463     case TextureManager::MASK_APPLYING:
464     case TextureManager::MASK_APPLIED:
465     {
466       ObserveTexture( textureInfo, observer );
467       break;
468     }
469     case TextureManager::UPLOADED:
470     {
471       if( observer )
472       {
473         LoadOrQueueTexture( textureInfo, observer );
474       }
475       break;
476     }
477     case TextureManager::CANCELLED:
478     {
479       // A cancelled texture hasn't finished loading yet. Treat as a loading texture
480       // (it's ref count has already been incremented, above)
481       textureInfo.loadState = TextureManager::LOADING;
482       ObserveTexture( textureInfo, observer );
483       break;
484     }
485     case TextureManager::LOAD_FINISHED:
486     {
487       // Loading has already completed.
488       if( observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER )
489       {
490         LoadOrQueueTexture( textureInfo, observer );
491       }
492       break;
493     }
494   }
495
496   // Return the TextureId for which this Texture can now be referenced by externally.
497   return textureId;
498 }
499
500 void TextureManager::Remove( const TextureManager::TextureId textureId, TextureUploadObserver* observer )
501 {
502   int textureInfoIndex = GetCacheIndexFromId( textureId );
503   if( textureInfoIndex != INVALID_INDEX )
504   {
505     TextureInfo& textureInfo( mTextureInfoContainer[ textureInfoIndex ] );
506
507     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise,
508                    "TextureManager::Remove(%d) url:%s\n  cacheIdx:%d loadState:%s reference count = %d\n",
509                    textureId, textureInfo.url.GetUrl().c_str(),
510                    textureInfoIndex, GET_LOAD_STATE_STRING( textureInfo.loadState ), textureInfo.referenceCount );
511
512     // Decrement the reference count and check if this is the last user of this Texture.
513     if( --textureInfo.referenceCount <= 0 )
514     {
515       // This is the last remove for this Texture.
516       textureInfo.referenceCount = 0;
517       bool removeTextureInfo = false;
518
519       // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
520       if( textureInfo.loadState == UPLOADED )
521       {
522         if( textureInfo.atlas )
523         {
524           textureInfo.atlas.Remove( textureInfo.atlasRect );
525         }
526         removeTextureInfo = true;
527       }
528       else if( textureInfo.loadState == LOADING )
529       {
530         // We mark the textureInfo for removal.
531         // Once the load has completed, this method will be called again.
532         textureInfo.loadState = CANCELLED;
533       }
534       else
535       {
536         // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
537         removeTextureInfo = true;
538       }
539
540       // If the state allows us to remove the TextureInfo data, we do so.
541       if( removeTextureInfo )
542       {
543         // Permanently remove the textureInfo struct.
544         mTextureInfoContainer.erase( mTextureInfoContainer.begin() + textureInfoIndex );
545       }
546     }
547
548     if( observer )
549     {
550       // Remove element from the LoadQueue
551       for( auto&& element : mLoadQueue )
552       {
553         if( element.mObserver == observer )
554         {
555           mLoadQueue.Erase( &element );
556           break;
557         }
558       }
559     }
560   }
561 }
562
563 VisualUrl TextureManager::GetVisualUrl( TextureId textureId )
564 {
565   VisualUrl visualUrl("");
566   int cacheIndex = GetCacheIndexFromId( textureId );
567
568   if( cacheIndex != INVALID_CACHE_INDEX )
569   {
570     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::GetVisualUrl. Using cached texture id=%d, textureId=%d\n",
571                    cacheIndex, textureId );
572
573     TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
574     visualUrl = cachedTextureInfo.url;
575   }
576   return visualUrl;
577 }
578
579 TextureManager::LoadState TextureManager::GetTextureState( TextureId textureId )
580 {
581   LoadState loadState = TextureManager::NOT_STARTED;
582
583   int cacheIndex = GetCacheIndexFromId( textureId );
584   if( cacheIndex != INVALID_CACHE_INDEX )
585   {
586     TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
587     loadState = cachedTextureInfo.loadState;
588   }
589   else
590   {
591     for( auto&& elem : mExternalTextures )
592     {
593       if( elem.textureId == textureId )
594       {
595         loadState = LoadState::UPLOADED;
596         break;
597       }
598     }
599   }
600   return loadState;
601 }
602
603 TextureManager::LoadState TextureManager::GetTextureStateInternal( TextureId textureId )
604 {
605   LoadState loadState = TextureManager::NOT_STARTED;
606
607   int cacheIndex = GetCacheIndexFromId( textureId );
608   if( cacheIndex != INVALID_CACHE_INDEX )
609   {
610     TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
611     loadState = cachedTextureInfo.loadState;
612   }
613
614   return loadState;
615 }
616
617 TextureSet TextureManager::GetTextureSet( TextureId textureId )
618 {
619   TextureSet textureSet;// empty handle
620
621   int cacheIndex = GetCacheIndexFromId( textureId );
622   if( cacheIndex != INVALID_CACHE_INDEX )
623   {
624     TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
625     textureSet = cachedTextureInfo.textureSet;
626   }
627   else
628   {
629     for( auto&& elem : mExternalTextures )
630     {
631       if( elem.textureId == textureId )
632       {
633         textureSet = elem.textureSet;
634         break;
635       }
636     }
637   }
638   return textureSet;
639 }
640
641 std::string TextureManager::AddExternalTexture( TextureSet& textureSet )
642 {
643   TextureManager::ExternalTextureInfo info;
644   info.textureId = GenerateUniqueTextureId();
645   info.textureSet = textureSet;
646   mExternalTextures.emplace_back( info );
647   return VisualUrl::CreateTextureUrl( std::to_string( info.textureId ) );
648 }
649
650 TextureSet TextureManager::RemoveExternalTexture( const std::string& url )
651 {
652   if( url.size() > 0u )
653   {
654     // get the location from the Url
655     VisualUrl parseUrl( url );
656     if( VisualUrl::TEXTURE == parseUrl.GetProtocolType() )
657     {
658       std::string location = parseUrl.GetLocation();
659       if( location.size() > 0u )
660       {
661         TextureId id = std::stoi( location );
662         const auto end = mExternalTextures.end();
663         for( auto iter = mExternalTextures.begin(); iter != end; ++iter )
664         {
665           if( iter->textureId == id )
666           {
667             auto textureSet = iter->textureSet;
668             mExternalTextures.erase( iter );
669             return textureSet;
670           }
671         }
672       }
673     }
674   }
675   return TextureSet();
676 }
677
678
679 void TextureManager::AddObserver( TextureManager::LifecycleObserver& observer )
680 {
681   // make sure an observer doesn't observe the same object twice
682   // otherwise it will get multiple calls to ObjectDestroyed()
683   DALI_ASSERT_DEBUG( mLifecycleObservers.End() == std::find( mLifecycleObservers.Begin(), mLifecycleObservers.End(), &observer));
684   mLifecycleObservers.PushBack( &observer );
685 }
686
687 void TextureManager::RemoveObserver( TextureManager::LifecycleObserver& observer)
688 {
689   // Find the observer...
690   auto endIter =  mLifecycleObservers.End();
691   for( auto iter = mLifecycleObservers.Begin(); iter != endIter; ++iter)
692   {
693     if( (*iter) == &observer)
694     {
695       mLifecycleObservers.Erase( iter );
696       break;
697     }
698   }
699   DALI_ASSERT_DEBUG(endIter != mLifecycleObservers.End());
700 }
701
702 void TextureManager::LoadOrQueueTexture( TextureInfo& textureInfo, TextureUploadObserver* observer )
703 {
704   switch( textureInfo.loadState )
705   {
706     case NOT_STARTED:
707     case LOAD_FAILED:
708     {
709       if( mQueueLoadFlag )
710       {
711         QueueLoadTexture( textureInfo, observer );
712       }
713       else
714       {
715         LoadTexture( textureInfo, observer );
716       }
717       break;
718     }
719     case UPLOADED:
720     {
721       if( mQueueLoadFlag )
722       {
723         QueueLoadTexture( textureInfo, observer );
724       }
725       else
726       {
727         // The Texture has already loaded. The other observers have already been notified.
728         // We need to send a "late" loaded notification for this observer.
729         observer->UploadComplete( true, textureInfo.textureId, textureInfo.textureSet,
730                                   textureInfo.useAtlas, textureInfo.atlasRect,
731                                   textureInfo.preMultiplied );
732       }
733       break;
734     }
735     case LOADING:
736     case CANCELLED:
737     case LOAD_FINISHED:
738     case WAITING_FOR_MASK:
739     case MASK_APPLYING:
740     case MASK_APPLIED:
741     {
742       break;
743     }
744   }
745 }
746
747 void TextureManager::QueueLoadTexture( TextureInfo& textureInfo, TextureUploadObserver* observer )
748 {
749   auto textureId = textureInfo.textureId;
750   mLoadQueue.PushBack( LoadQueueElement( textureId, observer) );
751 }
752
753 void TextureManager::LoadTexture( TextureInfo& textureInfo, TextureUploadObserver* observer )
754 {
755   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::LoadTexture(): url:%s sync:%s\n",
756                  textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously?"T":"F" );
757
758   textureInfo.loadState = LOADING;
759   if( !textureInfo.loadSynchronously )
760   {
761     auto& loadersContainer = textureInfo.url.IsLocalResource() ? mAsyncLocalLoaders : mAsyncRemoteLoaders;
762     auto loadingHelperIt = loadersContainer.GetNext();
763     auto premultiplyOnLoad = ( textureInfo.preMultiplyOnLoad && textureInfo.maskTextureId == INVALID_TEXTURE_ID ) ?
764                                DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF;
765     DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End());
766     loadingHelperIt->Load(textureInfo.textureId, textureInfo.url,
767                           textureInfo.desiredSize, textureInfo.fittingMode,
768                           textureInfo.samplingMode, textureInfo.orientationCorrection,
769                           premultiplyOnLoad );
770   }
771   ObserveTexture( textureInfo, observer );
772 }
773
774 void TextureManager::ProcessQueuedTextures()
775 {
776   for( auto&& element : mLoadQueue )
777   {
778     int cacheIndex = GetCacheIndexFromId( element.mTextureId );
779     if( cacheIndex != INVALID_CACHE_INDEX )
780     {
781       TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
782       if( textureInfo.loadState == UPLOADED )
783       {
784         element.mObserver->UploadComplete( true, textureInfo.textureId, textureInfo.textureSet,
785                                            textureInfo.useAtlas, textureInfo.atlasRect,
786                                            textureInfo.preMultiplied );
787       }
788       else if ( textureInfo.loadState == LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER )
789       {
790         element.mObserver->LoadComplete( true, textureInfo.pixelBuffer, textureInfo.url, textureInfo.preMultiplied );
791       }
792       else
793       {
794         LoadTexture( textureInfo, element.mObserver );
795       }
796     }
797   }
798   mLoadQueue.Clear();
799 }
800
801 void TextureManager::ObserveTexture( TextureInfo& textureInfo,
802                                      TextureUploadObserver* observer )
803 {
804   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::ObserveTexture(): url:%s observer:%p\n",
805                  textureInfo.url.GetUrl().c_str(), observer );
806
807   if( observer )
808   {
809     textureInfo.observerList.PushBack( observer );
810     observer->DestructionSignal().Connect( this, &TextureManager::ObserverDestroyed );
811   }
812 }
813
814 void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingContainer, uint32_t id,
815                                         Devel::PixelBuffer pixelBuffer )
816 {
817   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id );
818
819   if( loadingContainer.size() >= 1u )
820   {
821     AsyncLoadingInfo loadingInfo = loadingContainer.front();
822
823     if( loadingInfo.loadId == id )
824     {
825       int cacheIndex = GetCacheIndexFromId( loadingInfo.textureId );
826       if( cacheIndex != INVALID_CACHE_INDEX )
827       {
828         TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
829
830         DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise,
831                        "  textureId:%d Url:%s CacheIndex:%d LoadState: %d\n",
832                        textureInfo.textureId, textureInfo.url.GetUrl().c_str(), cacheIndex, textureInfo.loadState );
833
834         if( textureInfo.loadState != CANCELLED )
835         {
836           // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified)
837           PostLoad( textureInfo, pixelBuffer );
838         }
839         else
840         {
841           Remove( textureInfo.textureId, nullptr );
842         }
843       }
844     }
845
846     loadingContainer.pop_front();
847   }
848 }
849
850 void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer )
851 {
852   // Was the load successful?
853   if( pixelBuffer && ( pixelBuffer.GetWidth() != 0 ) && ( pixelBuffer.GetHeight() != 0 ) )
854   {
855     // No atlas support for now
856     textureInfo.useAtlas = NO_ATLAS;
857     textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied();
858
859     if( textureInfo.storageType == UPLOAD_TO_TEXTURE )
860     {
861       // If there is a mask texture ID associated with this texture, then apply the mask
862       // if it's already loaded. If it hasn't, and the mask is still loading,
863       // wait for the mask to finish loading.
864       if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
865       {
866         if( textureInfo.loadState == MASK_APPLYING )
867         {
868           textureInfo.loadState = MASK_APPLIED;
869           UploadTexture( pixelBuffer, textureInfo );
870           NotifyObservers( textureInfo, true );
871         }
872         else
873         {
874           LoadState maskLoadState = GetTextureStateInternal( textureInfo.maskTextureId );
875           textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily
876           if( maskLoadState == LOADING )
877           {
878             textureInfo.loadState = WAITING_FOR_MASK;
879           }
880           else if( maskLoadState == LOAD_FINISHED )
881           {
882             // Send New Task to Thread
883             ApplyMask( textureInfo, textureInfo.maskTextureId );
884           }
885         }
886       }
887       else
888       {
889         UploadTexture( pixelBuffer, textureInfo );
890         NotifyObservers( textureInfo, true );
891       }
892     }
893     else
894     {
895       textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
896       textureInfo.loadState = LOAD_FINISHED;
897
898       if( textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER )
899       {
900         NotifyObservers( textureInfo, true );
901       }
902       else
903       {
904         // Check if there was another texture waiting for this load to complete
905         // (e.g. if this was an image mask, and its load is on a different thread)
906         CheckForWaitingTexture( textureInfo );
907       }
908     }
909   }
910   else
911   {
912     // @todo If the load was unsuccessful, upload the broken image.
913     textureInfo.loadState = LOAD_FAILED;
914     CheckForWaitingTexture( textureInfo );
915     NotifyObservers( textureInfo, false );
916   }
917 }
918
919 void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo )
920 {
921   // Search the cache, checking if any texture has this texture id as a
922   // maskTextureId:
923   const unsigned int size = mTextureInfoContainer.size();
924
925   for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
926   {
927     if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
928         mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
929     {
930       TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
931
932       if( maskTextureInfo.loadState == LOAD_FINISHED )
933       {
934         // Send New Task to Thread
935         ApplyMask( textureInfo, maskTextureInfo.textureId );
936       }
937       else
938       {
939         textureInfo.pixelBuffer.Reset();
940         textureInfo.loadState = LOAD_FAILED;
941         NotifyObservers( textureInfo, false );
942       }
943     }
944   }
945 }
946
947 void TextureManager::ApplyMask( TextureInfo& textureInfo, TextureId maskTextureId )
948 {
949   int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
950   if( maskCacheIndex != INVALID_CACHE_INDEX )
951   {
952     Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
953     Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer;
954     textureInfo.pixelBuffer.Reset();
955
956     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::ApplyMask(): url:%s sync:%s\n",
957                    textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously?"T":"F" );
958
959     textureInfo.loadState = MASK_APPLYING;
960     auto& loadersContainer = textureInfo.url.IsLocalResource() ? mAsyncLocalLoaders : mAsyncRemoteLoaders;
961     auto loadingHelperIt = loadersContainer.GetNext();
962     auto premultiplyOnLoad = textureInfo.preMultiplyOnLoad ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF;
963     DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End());
964     loadingHelperIt->ApplyMask( textureInfo.textureId, pixelBuffer, maskPixelBuffer, textureInfo.scaleFactor, textureInfo.cropToMask, premultiplyOnLoad );
965   }
966 }
967
968 void TextureManager::UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo )
969 {
970   if( textureInfo.useAtlas != USE_ATLAS )
971   {
972     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::General, "  TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
973
974     // Check if this pixelBuffer is premultiplied
975     textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied();
976
977     Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(),
978                                     pixelBuffer.GetWidth(), pixelBuffer.GetHeight() );
979
980     PixelData pixelData = Devel::PixelBuffer::Convert( pixelBuffer );
981     texture.Upload( pixelData );
982     if ( ! textureInfo.textureSet )
983     {
984       textureInfo.textureSet = TextureSet::New();
985     }
986     textureInfo.textureSet.SetTexture( 0u, texture );
987   }
988
989   // Update the load state.
990   // Note: This is regardless of success as we care about whether a
991   // load attempt is in progress or not.  If unsuccessful, a broken
992   // image is still loaded.
993   textureInfo.loadState = UPLOADED;
994 }
995
996 void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
997 {
998   TextureId textureId = textureInfo.textureId;
999
1000   // If there is an observer: Notify the load is complete, whether successful or not,
1001   // and erase it from the list
1002   TextureInfo* info = &textureInfo;
1003
1004   mQueueLoadFlag = true;
1005
1006   while( info->observerList.Count() )
1007   {
1008     TextureUploadObserver* observer = info->observerList[0];
1009
1010     // During UploadComplete() a Control ResourceReady() signal is emitted.
1011     // During that signal the app may add remove /add Textures (e.g. via
1012     // ImageViews).
1013     // It is possible for observers to be removed from the observer list,
1014     // and it is also possible for the mTextureInfoContainer to be modified,
1015     // invalidating the reference to the textureInfo struct.
1016     // Texture load requests for the same URL are deferred until the end of this
1017     // method.
1018     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "NotifyObservers() url:%s loadState:%s\n",
1019                    textureInfo.url.GetUrl().c_str(), GET_LOAD_STATE_STRING(textureInfo.loadState ) );
1020
1021     // It is possible for the observer to be deleted.
1022     // Disconnect and remove the observer first.
1023     observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed );
1024
1025     info->observerList.Erase( info->observerList.begin() );
1026
1027     if( info->storageType == StorageType::RETURN_PIXEL_BUFFER )
1028     {
1029       observer->LoadComplete( success, info->pixelBuffer, info->url, info->preMultiplied );
1030     }
1031     else
1032     {
1033       observer->UploadComplete( success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect,
1034                                 info->preMultiplied );
1035     }
1036
1037     // Get the textureInfo from the container again as it may have been invalidated.
1038     int textureInfoIndex = GetCacheIndexFromId( textureId );
1039     if( textureInfoIndex == INVALID_CACHE_INDEX)
1040     {
1041       break; // texture has been removed - can stop.
1042     }
1043     info = &mTextureInfoContainer[ textureInfoIndex ];
1044   }
1045
1046   mQueueLoadFlag = false;
1047   ProcessQueuedTextures();
1048
1049   if( info->storageType == StorageType::RETURN_PIXEL_BUFFER && info->observerList.Count() == 0 )
1050   {
1051     Remove( info->textureId, nullptr );
1052   }
1053 }
1054
1055 TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
1056 {
1057   return mCurrentTextureId++;
1058 }
1059
1060 int TextureManager::GetCacheIndexFromId( const TextureId textureId )
1061 {
1062   const unsigned int size = mTextureInfoContainer.size();
1063
1064   for( unsigned int i = 0; i < size; ++i )
1065   {
1066     if( mTextureInfoContainer[i].textureId == textureId )
1067     {
1068       return i;
1069     }
1070   }
1071
1072   return INVALID_CACHE_INDEX;
1073 }
1074
1075 TextureManager::TextureHash TextureManager::GenerateHash(
1076   const std::string&             url,
1077   const ImageDimensions          size,
1078   const FittingMode::Type        fittingMode,
1079   const Dali::SamplingMode::Type samplingMode,
1080   const UseAtlas                 useAtlas,
1081   TextureId                      maskTextureId )
1082 {
1083   std::string hashTarget( url );
1084   const size_t urlLength = hashTarget.length();
1085   const uint16_t width = size.GetWidth();
1086   const uint16_t height = size.GetWidth();
1087
1088   // If either the width or height has been specified, include the resizing options in the hash
1089   if( width != 0 || height != 0 )
1090   {
1091     // We are appending 5 bytes to the URL to form the hash input.
1092     hashTarget.resize( urlLength + 5u );
1093     char* hashTargetPtr = &( hashTarget[ urlLength ] );
1094
1095     // Pack the width and height (4 bytes total).
1096     *hashTargetPtr++ = size.GetWidth() & 0xff;
1097     *hashTargetPtr++ = ( size.GetWidth() >> 8u ) & 0xff;
1098     *hashTargetPtr++ = size.GetHeight() & 0xff;
1099     *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
1100
1101     // Bit-pack the FittingMode, SamplingMode and atlasing.
1102     // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
1103     *hashTargetPtr   = ( fittingMode << 4u ) | ( samplingMode << 1 ) | useAtlas;
1104   }
1105   else
1106   {
1107     // We are not including sizing information, but we still need an extra byte for atlasing.
1108     hashTarget.resize( urlLength + 1u );
1109
1110     // Add the atlasing to the hash input.
1111     switch( useAtlas )
1112     {
1113       case UseAtlas::NO_ATLAS:
1114       {
1115         hashTarget[ urlLength ] = 'f';
1116         break;
1117       }
1118       case UseAtlas::USE_ATLAS:
1119       {
1120         hashTarget[ urlLength ] = 't';
1121         break;
1122       }
1123     }
1124   }
1125
1126   if( maskTextureId != INVALID_TEXTURE_ID )
1127   {
1128     auto textureIdIndex = hashTarget.length();
1129     hashTarget.resize( hashTarget.length() + sizeof( TextureId ) );
1130     unsigned char* hashTargetPtr = reinterpret_cast<unsigned char*>(&( hashTarget[ textureIdIndex ] ));
1131
1132     // Append the texture id to the end of the URL byte by byte:
1133     // (to avoid SIGBUS / alignment issues)
1134     for( size_t byteIter = 0; byteIter < sizeof( TextureId ); ++byteIter )
1135     {
1136       *hashTargetPtr++ = maskTextureId & 0xff;
1137       maskTextureId >>= 8u;
1138     }
1139   }
1140
1141   return Dali::CalculateHash( hashTarget );
1142 }
1143
1144 int TextureManager::FindCachedTexture(
1145   const TextureManager::TextureHash hash,
1146   const std::string&                url,
1147   const ImageDimensions             size,
1148   const FittingMode::Type           fittingMode,
1149   const Dali::SamplingMode::Type    samplingMode,
1150   const bool                        useAtlas,
1151   TextureId                         maskTextureId,
1152   TextureManager::MultiplyOnLoad    preMultiplyOnLoad,
1153   StorageType                       storageType )
1154 {
1155   // Default to an invalid ID, in case we do not find a match.
1156   int cacheIndex = INVALID_CACHE_INDEX;
1157
1158   // Iterate through our hashes to find a match.
1159   const unsigned int count = mTextureInfoContainer.size();
1160   for( unsigned int i = 0u; i < count; ++i )
1161   {
1162     if( mTextureInfoContainer[i].hash == hash )
1163     {
1164       // We have a match, now we check all the original parameters in case of a hash collision.
1165       TextureInfo& textureInfo( mTextureInfoContainer[i] );
1166
1167       if( ( url == textureInfo.url.GetUrl() ) &&
1168           ( useAtlas == textureInfo.useAtlas ) &&
1169           ( maskTextureId == textureInfo.maskTextureId ) &&
1170           ( size == textureInfo.desiredSize ) &&
1171           ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
1172             ( fittingMode == textureInfo.fittingMode &&
1173               samplingMode == textureInfo.samplingMode ) ) &&
1174           ( storageType == textureInfo.storageType ) )
1175       {
1176         // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
1177         // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
1178         if( ( preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad )
1179             || ( preMultiplyOnLoad == TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied ) )
1180         {
1181           // The found Texture is a match.
1182           cacheIndex = i;
1183           break;
1184         }
1185       }
1186     }
1187   }
1188
1189   return cacheIndex;
1190 }
1191
1192 void TextureManager::ObserverDestroyed( TextureUploadObserver* observer )
1193 {
1194   const unsigned int count = mTextureInfoContainer.size();
1195   for( unsigned int i = 0; i < count; ++i )
1196   {
1197     TextureInfo& textureInfo( mTextureInfoContainer[i] );
1198     for( TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin();
1199          j != textureInfo.observerList.End(); )
1200     {
1201       if( *j == observer )
1202       {
1203         j = textureInfo.observerList.Erase( j );
1204       }
1205       else
1206       {
1207         ++j;
1208       }
1209     }
1210   }
1211 }
1212
1213
1214 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureManager)
1215 : AsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager,
1216                      AsyncLoadingInfoContainerType())
1217 {
1218 }
1219
1220 void TextureManager::AsyncLoadingHelper::Load(TextureId          textureId,
1221                                               const VisualUrl&   url,
1222                                               ImageDimensions    desiredSize,
1223                                               FittingMode::Type  fittingMode,
1224                                               SamplingMode::Type samplingMode,
1225                                               bool               orientationCorrection,
1226                                               DevelAsyncImageLoader::PreMultiplyOnLoad  preMultiplyOnLoad)
1227 {
1228   mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId));
1229   auto id = DevelAsyncImageLoader::Load( mLoader, url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad );
1230   mLoadingInfoContainer.back().loadId = id;
1231 }
1232
1233 void TextureManager::AsyncLoadingHelper::ApplyMask( TextureId textureId,
1234                                                     Devel::PixelBuffer pixelBuffer,
1235                                                     Devel::PixelBuffer maskPixelBuffer,
1236                                                     float contentScale,
1237                                                     bool cropToMask,
1238                                                     DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad )
1239 {
1240   mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId));
1241   auto id = DevelAsyncImageLoader::ApplyMask( mLoader, pixelBuffer, maskPixelBuffer, contentScale, cropToMask, preMultiplyOnLoad );
1242   mLoadingInfoContainer.back().loadId = id;
1243 }
1244
1245 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(AsyncLoadingHelper&& rhs)
1246 : AsyncLoadingHelper(rhs.mLoader, rhs.mTextureManager, std::move(rhs.mLoadingInfoContainer))
1247 {
1248 }
1249
1250 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(
1251     Toolkit::AsyncImageLoader loader,
1252     TextureManager& textureManager,
1253     AsyncLoadingInfoContainerType&& loadingInfoContainer)
1254 : mLoader(loader),
1255   mTextureManager(textureManager),
1256   mLoadingInfoContainer(std::move(loadingInfoContainer))
1257 {
1258   DevelAsyncImageLoader::PixelBufferLoadedSignal(mLoader).Connect(
1259       this, &AsyncLoadingHelper::AsyncLoadComplete);
1260 }
1261
1262 void TextureManager::AsyncLoadingHelper::AsyncLoadComplete(uint32_t           id,
1263                                                            Devel::PixelBuffer pixelBuffer )
1264 {
1265   mTextureManager.AsyncLoadComplete( mLoadingInfoContainer, id, pixelBuffer );
1266 }
1267
1268 void TextureManager::SetBrokenImageUrl(const std::string& brokenImageUrl)
1269 {
1270   mBrokenImageUrl = brokenImageUrl;
1271 }
1272
1273 } // namespace Internal
1274
1275 } // namespace Toolkit
1276
1277 } // namespace Dali