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