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