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