Merge "Rendering optimisation with AddOn" 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::NOT_STARTED ? "NOT_STARTED" :             \
86     loadState == TextureManager::LOADING ? "LOADING" :                   \
87     loadState == TextureManager::LOAD_FINISHED ? "LOAD_FINISHED" :       \
88     loadState == TextureManager::WAITING_FOR_MASK ? "WAITING_FOR_MASK" : \
89     loadState == TextureManager::MASK_APPLYING ? "MASK_APPLYING" :         \
90     loadState == TextureManager::MASK_APPLIED ? "MASK_APPLIED" :         \
91     loadState == TextureManager::UPLOADED ? "UPLOADED" :                 \
92     loadState == TextureManager::CANCELLED ? "CANCELLED" :               \
93     loadState == TextureManager::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, UPLOAD_TO_TEXTURE, textureObserver,
196                                      true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex );
197     TextureManager::LoadState loadState = GetTextureStateInternal( textureId );
198     if( loadState == TextureManager::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, 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::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::LOADING ||
371                         loadState == TextureManager::WAITING_FOR_MASK ||
372                         loadState == TextureManager::MASK_APPLYING ||
373                         loadState == TextureManager::MASK_APPLIED ||
374                         loadState == TextureManager::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, 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, 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, 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::LOADING != textureInfo.loadState &&
512        TextureManager::WAITING_FOR_MASK != textureInfo.loadState &&
513        TextureManager::MASK_APPLYING != textureInfo.loadState &&
514        TextureManager::MASK_APPLIED != textureInfo.loadState &&
515        TextureManager::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::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::LOAD_FAILED: // Failed notifies observer which then stops observing.
528     case TextureManager::NOT_STARTED:
529     {
530       LoadOrQueueTexture( textureInfo, observer ); // If called inside NotifyObservers, queues until afterwards
531       break;
532     }
533     case TextureManager::LOADING:
534     case TextureManager::WAITING_FOR_MASK:
535     case TextureManager::MASK_APPLYING:
536     case TextureManager::MASK_APPLIED:
537     {
538       ObserveTexture( textureInfo, observer );
539       break;
540     }
541     case TextureManager::UPLOADED:
542     {
543       if( observer )
544       {
545         LoadOrQueueTexture( textureInfo, observer );
546       }
547       break;
548     }
549     case TextureManager::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::LOADING;
554       ObserveTexture( textureInfo, observer );
555       break;
556     }
557     case TextureManager::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 == UPLOADED )
593       {
594         if( textureInfo.atlas )
595         {
596           textureInfo.atlas.Remove( textureInfo.atlasRect );
597         }
598         removeTextureInfo = true;
599       }
600       else if( textureInfo.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 = 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::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::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 NOT_STARTED:
778     case 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 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 LOADING:
807     case CANCELLED:
808     case LOAD_FINISHED:
809     case WAITING_FOR_MASK:
810     case MASK_APPLYING:
811     case 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
824 void TextureManager::LoadTexture( TextureInfo& textureInfo, TextureUploadObserver* observer )
825 {
826   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::LoadTexture(): url:%s sync:%s\n",
827                  textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously?"T":"F" );
828
829   textureInfo.loadState = LOADING;
830   if( !textureInfo.loadSynchronously )
831   {
832     auto& loadersContainer = textureInfo.url.IsLocalResource() ? mAsyncLocalLoaders : mAsyncRemoteLoaders;
833     auto loadingHelperIt = loadersContainer.GetNext();
834     auto premultiplyOnLoad = ( textureInfo.preMultiplyOnLoad && textureInfo.maskTextureId == INVALID_TEXTURE_ID ) ?
835                                DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF;
836     DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End());
837     if( textureInfo.animatedImageLoading )
838     {
839       loadingHelperIt->LoadAnimatedImage( textureInfo.textureId, textureInfo.animatedImageLoading, textureInfo.frameIndex );
840     }
841     else
842     {
843       loadingHelperIt->Load(textureInfo.textureId, textureInfo.url,
844                             textureInfo.desiredSize, textureInfo.fittingMode,
845                             textureInfo.samplingMode, textureInfo.orientationCorrection,
846                             premultiplyOnLoad );
847     }
848   }
849   ObserveTexture( textureInfo, observer );
850 }
851
852 void TextureManager::ProcessQueuedTextures()
853 {
854   for( auto&& element : mLoadQueue )
855   {
856     int cacheIndex = GetCacheIndexFromId( element.mTextureId );
857     if( cacheIndex != INVALID_CACHE_INDEX )
858     {
859       TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
860       if( textureInfo.loadState == UPLOADED )
861       {
862         element.mObserver->UploadComplete( true, textureInfo.textureId, textureInfo.textureSet,
863                                            textureInfo.useAtlas, textureInfo.atlasRect,
864                                            textureInfo.preMultiplied );
865       }
866       else if ( textureInfo.loadState == LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER )
867       {
868         element.mObserver->LoadComplete( true, textureInfo.pixelBuffer, textureInfo.url, textureInfo.preMultiplied );
869       }
870       else
871       {
872         LoadTexture( textureInfo, element.mObserver );
873       }
874     }
875   }
876   mLoadQueue.Clear();
877 }
878
879 void TextureManager::ObserveTexture( TextureInfo& textureInfo,
880                                      TextureUploadObserver* observer )
881 {
882   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::ObserveTexture(): url:%s observer:%p\n",
883                  textureInfo.url.GetUrl().c_str(), observer );
884
885   if( observer )
886   {
887     textureInfo.observerList.PushBack( observer );
888     observer->DestructionSignal().Connect( this, &TextureManager::ObserverDestroyed );
889   }
890 }
891
892 void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingContainer, uint32_t id,
893                                         Devel::PixelBuffer pixelBuffer )
894 {
895   DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id );
896
897   if( loadingContainer.size() >= 1u )
898   {
899     AsyncLoadingInfo loadingInfo = loadingContainer.front();
900
901     if( loadingInfo.loadId == id )
902     {
903       int cacheIndex = GetCacheIndexFromId( loadingInfo.textureId );
904       if( cacheIndex != INVALID_CACHE_INDEX )
905       {
906         TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
907
908         DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise,
909                        "  textureId:%d Url:%s CacheIndex:%d LoadState: %d\n",
910                        textureInfo.textureId, textureInfo.url.GetUrl().c_str(), cacheIndex, textureInfo.loadState );
911
912         if( textureInfo.loadState != CANCELLED )
913         {
914           // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified)
915           PostLoad( textureInfo, pixelBuffer );
916         }
917         else
918         {
919           Remove( textureInfo.textureId, nullptr );
920         }
921       }
922     }
923
924     loadingContainer.pop_front();
925   }
926 }
927
928 void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer )
929 {
930   // Was the load successful?
931   if( pixelBuffer && ( pixelBuffer.GetWidth() != 0 ) && ( pixelBuffer.GetHeight() != 0 ) )
932   {
933     // No atlas support for now
934     textureInfo.useAtlas = NO_ATLAS;
935     textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied();
936
937     if( textureInfo.storageType == UPLOAD_TO_TEXTURE )
938     {
939       // If there is a mask texture ID associated with this texture, then apply the mask
940       // if it's already loaded. If it hasn't, and the mask is still loading,
941       // wait for the mask to finish loading.
942       if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
943       {
944         if( textureInfo.loadState == MASK_APPLYING )
945         {
946           textureInfo.loadState = MASK_APPLIED;
947           UploadTexture( pixelBuffer, textureInfo );
948           NotifyObservers( textureInfo, true );
949         }
950         else
951         {
952           LoadState maskLoadState = GetTextureStateInternal( textureInfo.maskTextureId );
953           textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily
954           if( maskLoadState == LOADING )
955           {
956             textureInfo.loadState = WAITING_FOR_MASK;
957           }
958           else if( maskLoadState == LOAD_FINISHED )
959           {
960             // Send New Task to Thread
961             ApplyMask( textureInfo, textureInfo.maskTextureId );
962           }
963         }
964       }
965       else
966       {
967         UploadTexture( pixelBuffer, textureInfo );
968         NotifyObservers( textureInfo, true );
969       }
970     }
971     else
972     {
973       textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
974       textureInfo.loadState = LOAD_FINISHED;
975
976       if( textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER )
977       {
978         NotifyObservers( textureInfo, true );
979       }
980       else
981       {
982         // Check if there was another texture waiting for this load to complete
983         // (e.g. if this was an image mask, and its load is on a different thread)
984         CheckForWaitingTexture( textureInfo );
985       }
986     }
987   }
988   else
989   {
990     // @todo If the load was unsuccessful, upload the broken image.
991     textureInfo.loadState = LOAD_FAILED;
992     CheckForWaitingTexture( textureInfo );
993     NotifyObservers( textureInfo, false );
994   }
995 }
996
997 void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo )
998 {
999   // Search the cache, checking if any texture has this texture id as a
1000   // maskTextureId:
1001   const unsigned int size = mTextureInfoContainer.size();
1002
1003   for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
1004   {
1005     if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
1006         mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
1007     {
1008       TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
1009
1010       if( maskTextureInfo.loadState == LOAD_FINISHED )
1011       {
1012         // Send New Task to Thread
1013         ApplyMask( textureInfo, maskTextureInfo.textureId );
1014       }
1015       else
1016       {
1017         textureInfo.pixelBuffer.Reset();
1018         textureInfo.loadState = LOAD_FAILED;
1019         NotifyObservers( textureInfo, false );
1020       }
1021     }
1022   }
1023 }
1024
1025 void TextureManager::ApplyMask( TextureInfo& textureInfo, TextureId maskTextureId )
1026 {
1027   int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
1028   if( maskCacheIndex != INVALID_CACHE_INDEX )
1029   {
1030     Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
1031     Devel::PixelBuffer pixelBuffer = textureInfo.pixelBuffer;
1032     textureInfo.pixelBuffer.Reset();
1033
1034     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::ApplyMask(): url:%s sync:%s\n",
1035                    textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously?"T":"F" );
1036
1037     textureInfo.loadState = MASK_APPLYING;
1038     auto& loadersContainer = textureInfo.url.IsLocalResource() ? mAsyncLocalLoaders : mAsyncRemoteLoaders;
1039     auto loadingHelperIt = loadersContainer.GetNext();
1040     auto premultiplyOnLoad = textureInfo.preMultiplyOnLoad ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF;
1041     DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End());
1042     loadingHelperIt->ApplyMask( textureInfo.textureId, pixelBuffer, maskPixelBuffer, textureInfo.scaleFactor, textureInfo.cropToMask, premultiplyOnLoad );
1043   }
1044 }
1045
1046 void TextureManager::UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo )
1047 {
1048   if( textureInfo.useAtlas != USE_ATLAS )
1049   {
1050     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::General, "  TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
1051
1052     // Check if this pixelBuffer is premultiplied
1053     textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied();
1054
1055     auto& renderingAddOn = RenderingAddOn::Get();
1056     if( renderingAddOn.IsValid() )
1057     {
1058       renderingAddOn.CreateGeometry( textureInfo.textureId, pixelBuffer );
1059     }
1060
1061     Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(),
1062                                     pixelBuffer.GetWidth(), pixelBuffer.GetHeight() );
1063
1064     PixelData pixelData = Devel::PixelBuffer::Convert( pixelBuffer );
1065     texture.Upload( pixelData );
1066     if ( ! textureInfo.textureSet )
1067     {
1068       textureInfo.textureSet = TextureSet::New();
1069     }
1070     textureInfo.textureSet.SetTexture( 0u, texture );
1071   }
1072
1073   // Update the load state.
1074   // Note: This is regardless of success as we care about whether a
1075   // load attempt is in progress or not.  If unsuccessful, a broken
1076   // image is still loaded.
1077   textureInfo.loadState = UPLOADED;
1078 }
1079
1080 void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
1081 {
1082   TextureId textureId = textureInfo.textureId;
1083
1084   // If there is an observer: Notify the load is complete, whether successful or not,
1085   // and erase it from the list
1086   TextureInfo* info = &textureInfo;
1087
1088   mQueueLoadFlag = true;
1089
1090   while( info->observerList.Count() )
1091   {
1092     TextureUploadObserver* observer = info->observerList[0];
1093
1094     // During UploadComplete() a Control ResourceReady() signal is emitted.
1095     // During that signal the app may add remove /add Textures (e.g. via
1096     // ImageViews).
1097     // It is possible for observers to be removed from the observer list,
1098     // and it is also possible for the mTextureInfoContainer to be modified,
1099     // invalidating the reference to the textureInfo struct.
1100     // Texture load requests for the same URL are deferred until the end of this
1101     // method.
1102     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "NotifyObservers() url:%s loadState:%s\n",
1103                    textureInfo.url.GetUrl().c_str(), GET_LOAD_STATE_STRING(textureInfo.loadState ) );
1104
1105     // It is possible for the observer to be deleted.
1106     // Disconnect and remove the observer first.
1107     observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed );
1108
1109     info->observerList.Erase( info->observerList.begin() );
1110
1111     if( info->storageType == StorageType::RETURN_PIXEL_BUFFER )
1112     {
1113       observer->LoadComplete( success, info->pixelBuffer, info->url, info->preMultiplied );
1114     }
1115     else
1116     {
1117       observer->UploadComplete( success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect,
1118                                 info->preMultiplied );
1119     }
1120
1121     // Get the textureInfo from the container again as it may have been invalidated.
1122     int textureInfoIndex = GetCacheIndexFromId( textureId );
1123     if( textureInfoIndex == INVALID_CACHE_INDEX)
1124     {
1125       break; // texture has been removed - can stop.
1126     }
1127     info = &mTextureInfoContainer[ textureInfoIndex ];
1128   }
1129
1130   mQueueLoadFlag = false;
1131   ProcessQueuedTextures();
1132
1133   if( info->storageType == StorageType::RETURN_PIXEL_BUFFER && info->observerList.Count() == 0 )
1134   {
1135     Remove( info->textureId, nullptr );
1136   }
1137 }
1138
1139 TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
1140 {
1141   return mCurrentTextureId++;
1142 }
1143
1144 int TextureManager::GetCacheIndexFromId( const TextureId textureId )
1145 {
1146   const unsigned int size = mTextureInfoContainer.size();
1147
1148   for( unsigned int i = 0; i < size; ++i )
1149   {
1150     if( mTextureInfoContainer[i].textureId == textureId )
1151     {
1152       return i;
1153     }
1154   }
1155
1156   return INVALID_CACHE_INDEX;
1157 }
1158
1159 TextureManager::TextureHash TextureManager::GenerateHash(
1160   const std::string&             url,
1161   const ImageDimensions          size,
1162   const FittingMode::Type        fittingMode,
1163   const Dali::SamplingMode::Type samplingMode,
1164   const UseAtlas                 useAtlas,
1165   TextureId                      maskTextureId,
1166   StorageType                    storageType,
1167   bool                           isAnimationImage,
1168   uint32_t                       frameIndex )
1169 {
1170   std::string hashTarget( url );
1171   const size_t urlLength = hashTarget.length();
1172   const uint16_t width = size.GetWidth();
1173   const uint16_t height = size.GetWidth();
1174
1175   // If either the width or height has been specified, include the resizing options in the hash
1176   if( width != 0 || height != 0 )
1177   {
1178     // We are appending 5 bytes to the URL to form the hash input.
1179     hashTarget.resize( urlLength + 5u );
1180     char* hashTargetPtr = &( hashTarget[ urlLength ] );
1181
1182     // Pack the width and height (4 bytes total).
1183     *hashTargetPtr++ = size.GetWidth() & 0xff;
1184     *hashTargetPtr++ = ( size.GetWidth() >> 8u ) & 0xff;
1185     *hashTargetPtr++ = size.GetHeight() & 0xff;
1186     *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
1187
1188     // Bit-pack the FittingMode, SamplingMode and atlasing.
1189     // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit, storageType=2bits
1190     *hashTargetPtr   = ( fittingMode << 6u ) | ( samplingMode << 3 ) | ( useAtlas << 2 ) | storageType;
1191   }
1192   else
1193   {
1194     // We are not including sizing information, but we still need an extra byte for atlasing.
1195     hashTarget.resize( urlLength + 1u );
1196
1197     // Add the atlasing to the hash input.
1198     switch( useAtlas )
1199     {
1200       case UseAtlas::NO_ATLAS:
1201       {
1202         hashTarget[ urlLength ] = 'f';
1203         break;
1204       }
1205       case UseAtlas::USE_ATLAS:
1206       {
1207         hashTarget[ urlLength ] = 't';
1208         break;
1209       }
1210     }
1211   }
1212
1213   if( isAnimationImage )
1214   {
1215     auto textureIdIndex = hashTarget.length();
1216     hashTarget.resize( hashTarget.length() + sizeof( uint32_t ) );
1217     char* hashTargetPtr = &( hashTarget[ textureIdIndex ] );
1218
1219     for( size_t byteIter = 0; byteIter < sizeof( uint32_t ); ++byteIter )
1220     {
1221       *hashTargetPtr++ = frameIndex & 0xff;
1222       frameIndex >>= 8u;
1223     }
1224   }
1225
1226   if( maskTextureId != INVALID_TEXTURE_ID )
1227   {
1228     auto textureIdIndex = hashTarget.length();
1229     hashTarget.resize( hashTarget.length() + sizeof( TextureId ) );
1230     unsigned char* hashTargetPtr = reinterpret_cast<unsigned char*>(&( hashTarget[ textureIdIndex ] ));
1231
1232     // Append the texture id to the end of the URL byte by byte:
1233     // (to avoid SIGBUS / alignment issues)
1234     for( size_t byteIter = 0; byteIter < sizeof( TextureId ); ++byteIter )
1235     {
1236       *hashTargetPtr++ = maskTextureId & 0xff;
1237       maskTextureId >>= 8u;
1238     }
1239   }
1240
1241   return Dali::CalculateHash( hashTarget );
1242 }
1243
1244 int TextureManager::FindCachedTexture(
1245   const TextureManager::TextureHash hash,
1246   const std::string&                url,
1247   const ImageDimensions             size,
1248   const FittingMode::Type           fittingMode,
1249   const Dali::SamplingMode::Type    samplingMode,
1250   const bool                        useAtlas,
1251   TextureId                         maskTextureId,
1252   TextureManager::MultiplyOnLoad    preMultiplyOnLoad,
1253   StorageType                       storageType,
1254   bool                              isAnimatedImage,
1255   uint32_t                          frameIndex )
1256 {
1257   // Default to an invalid ID, in case we do not find a match.
1258   int cacheIndex = INVALID_CACHE_INDEX;
1259
1260   // Iterate through our hashes to find a match.
1261   const unsigned int count = mTextureInfoContainer.size();
1262   for( unsigned int i = 0u; i < count; ++i )
1263   {
1264     if( mTextureInfoContainer[i].hash == hash )
1265     {
1266       // We have a match, now we check all the original parameters in case of a hash collision.
1267       TextureInfo& textureInfo( mTextureInfoContainer[i] );
1268
1269       if( ( url == textureInfo.url.GetUrl() ) &&
1270           ( useAtlas == textureInfo.useAtlas ) &&
1271           ( maskTextureId == textureInfo.maskTextureId ) &&
1272           ( size == textureInfo.desiredSize ) &&
1273           ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
1274             ( fittingMode == textureInfo.fittingMode &&
1275               samplingMode == textureInfo.samplingMode ) ) &&
1276           ( storageType == textureInfo.storageType ) &&
1277           ( isAnimatedImage == ( ( textureInfo.animatedImageLoading ) ? true : false ) ) &&
1278           ( frameIndex == textureInfo.frameIndex ) )
1279       {
1280         // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
1281         // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
1282         if( ( preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad )
1283             || ( preMultiplyOnLoad == TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied ) )
1284         {
1285           // The found Texture is a match.
1286           cacheIndex = i;
1287           break;
1288         }
1289       }
1290     }
1291   }
1292
1293   return cacheIndex;
1294 }
1295
1296 void TextureManager::ObserverDestroyed( TextureUploadObserver* observer )
1297 {
1298   const unsigned int count = mTextureInfoContainer.size();
1299   for( unsigned int i = 0; i < count; ++i )
1300   {
1301     TextureInfo& textureInfo( mTextureInfoContainer[i] );
1302     for( TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin();
1303          j != textureInfo.observerList.End(); )
1304     {
1305       if( *j == observer )
1306       {
1307         j = textureInfo.observerList.Erase( j );
1308       }
1309       else
1310       {
1311         ++j;
1312       }
1313     }
1314   }
1315 }
1316
1317
1318 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureManager)
1319 : AsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager,
1320                      AsyncLoadingInfoContainerType())
1321 {
1322 }
1323
1324 void TextureManager::AsyncLoadingHelper::LoadAnimatedImage( TextureId                   textureId,
1325                                                             Dali::AnimatedImageLoading  animatedImageLoading,
1326                                                             uint32_t                    frameIndex )
1327 {
1328   mLoadingInfoContainer.push_back( AsyncLoadingInfo( textureId ) );
1329   auto id = DevelAsyncImageLoader::LoadAnimatedImage( mLoader, animatedImageLoading, frameIndex );
1330   mLoadingInfoContainer.back().loadId = id;
1331 }
1332
1333 void TextureManager::AsyncLoadingHelper::Load( TextureId                                textureId,
1334                                                const VisualUrl&                         url,
1335                                                ImageDimensions                          desiredSize,
1336                                                FittingMode::Type                        fittingMode,
1337                                                SamplingMode::Type                       samplingMode,
1338                                                bool                                     orientationCorrection,
1339                                                DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad )
1340 {
1341   mLoadingInfoContainer.push_back( AsyncLoadingInfo( textureId ) );
1342   auto id = DevelAsyncImageLoader::Load( mLoader, url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad );
1343   mLoadingInfoContainer.back().loadId = id;
1344 }
1345
1346 void TextureManager::AsyncLoadingHelper::ApplyMask( TextureId                                textureId,
1347                                                     Devel::PixelBuffer                       pixelBuffer,
1348                                                     Devel::PixelBuffer                       maskPixelBuffer,
1349                                                     float                                    contentScale,
1350                                                     bool                                     cropToMask,
1351                                                     DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad )
1352 {
1353   mLoadingInfoContainer.push_back( AsyncLoadingInfo( textureId ) );
1354   auto id = DevelAsyncImageLoader::ApplyMask( mLoader, pixelBuffer, maskPixelBuffer, contentScale, cropToMask, preMultiplyOnLoad );
1355   mLoadingInfoContainer.back().loadId = id;
1356 }
1357
1358 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(AsyncLoadingHelper&& rhs)
1359 : AsyncLoadingHelper(rhs.mLoader, rhs.mTextureManager, std::move(rhs.mLoadingInfoContainer))
1360 {
1361 }
1362
1363 TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(
1364     Toolkit::AsyncImageLoader loader,
1365     TextureManager& textureManager,
1366     AsyncLoadingInfoContainerType&& loadingInfoContainer)
1367 : mLoader(loader),
1368   mTextureManager(textureManager),
1369   mLoadingInfoContainer(std::move(loadingInfoContainer))
1370 {
1371   DevelAsyncImageLoader::PixelBufferLoadedSignal(mLoader).Connect(
1372       this, &AsyncLoadingHelper::AsyncLoadComplete);
1373 }
1374
1375 void TextureManager::AsyncLoadingHelper::AsyncLoadComplete(uint32_t           id,
1376                                                            Devel::PixelBuffer pixelBuffer )
1377 {
1378   mTextureManager.AsyncLoadComplete( mLoadingInfoContainer, id, pixelBuffer );
1379 }
1380
1381 void TextureManager::SetBrokenImageUrl(const std::string& brokenImageUrl)
1382 {
1383   mBrokenImageUrl = brokenImageUrl;
1384 }
1385
1386 Geometry TextureManager::GetRenderGeometry(TextureId textureId, uint32_t& frontElements, uint32_t& backElements )
1387 {
1388   return RenderingAddOn::Get().IsValid() ?
1389          RenderingAddOn::Get().GetGeometry( textureId, frontElements, backElements) :
1390          Geometry();
1391 }
1392
1393 } // namespace Internal
1394
1395 } // namespace Toolkit
1396
1397 } // namespace Dali