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