Remove Atlas parameter for TextureManager cache system
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / texture-manager / texture-manager-impl.cpp
1 /*
2  * Copyright (c) 2023 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/texture-manager/texture-manager-impl.h>
20
21 // EXTERNAL HEADERS
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
24 #include <dali/integration-api/adaptor-framework/adaptor.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
38 constexpr auto TEXTURE_INDEX      = 0u; ///< The Index for texture
39 constexpr auto MASK_TEXTURE_INDEX = 1u; ///< The Index for mask texture
40 } // namespace
41
42 namespace Dali
43 {
44 namespace Toolkit
45 {
46 namespace Internal
47 {
48 #ifdef DEBUG_ENABLED
49 // Define logfilter Internal namespace level so other files (e.g. texture-cache-manager.cpp) can also use this filter.
50 Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXTURE_MANAGER");
51
52 // clang-format off
53 #define GET_LOAD_STATE_STRING(loadState) \
54   loadState == TextureManagerType::LoadState::NOT_STARTED      ? "NOT_STARTED"      : \
55   loadState == TextureManagerType::LoadState::LOADING          ? "LOADING"          : \
56   loadState == TextureManagerType::LoadState::LOAD_FINISHED    ? "LOAD_FINISHED"    : \
57   loadState == TextureManagerType::LoadState::WAITING_FOR_MASK ? "WAITING_FOR_MASK" : \
58   loadState == TextureManagerType::LoadState::MASK_APPLYING    ? "MASK_APPLYING"    : \
59   loadState == TextureManagerType::LoadState::MASK_APPLIED     ? "MASK_APPLIED"     : \
60   loadState == TextureManagerType::LoadState::UPLOADED         ? "UPLOADED"         : \
61   loadState == TextureManagerType::LoadState::CANCELLED        ? "CANCELLED"        : \
62   loadState == TextureManagerType::LoadState::MASK_CANCELLED   ? "MASK_CANCELLED"   : \
63   loadState == TextureManagerType::LoadState::LOAD_FAILED      ? "LOAD_FAILED"      : \
64                                                                  "Unknown"
65 // clang-format on
66 #endif
67
68 namespace
69 {
70 const Vector4 FULL_ATLAS_RECT(0.0f, 0.0f, 1.0f, 1.0f); ///< UV Rectangle that covers the full Texture
71
72 void PreMultiply(Devel::PixelBuffer pixelBuffer, TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
73 {
74   if(Pixel::HasAlpha(pixelBuffer.GetPixelFormat()))
75   {
76     if(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD)
77     {
78       pixelBuffer.MultiplyColorByAlpha();
79     }
80   }
81   else
82   {
83     preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
84   }
85 }
86
87 } // Anonymous namespace
88
89 TextureManager::MaskingData::MaskingData()
90 : mAlphaMaskUrl(),
91   mAlphaMaskId(INVALID_TEXTURE_ID),
92   mContentScaleFactor(1.0f),
93   mCropToMask(true),
94   mPreappliedMasking(true),
95   mMaskImageLoadingFailed(false)
96 {
97 }
98
99 TextureManager::TextureManager(bool loadYuvPlanes)
100 : mTextureCacheManager(),
101   mAsyncLoader(std::unique_ptr<TextureAsyncLoadingHelper>(new TextureAsyncLoadingHelper(*this))),
102   mLifecycleObservers(),
103   mLoadQueue(),
104   mLoadingQueueTextureId(INVALID_TEXTURE_ID),
105   mRemoveQueue(),
106   mLoadYuvPlanes(loadYuvPlanes),
107   mRemoveProcessorRegistered(false)
108 {
109   // Initialize the AddOn
110   RenderingAddOn::Get();
111 }
112
113 TextureManager::~TextureManager()
114 {
115   if(mRemoveProcessorRegistered && Adaptor::IsAvailable())
116   {
117     Adaptor::Get().UnregisterProcessor(*this, true);
118     mRemoveProcessorRegistered = false;
119   }
120
121   for(auto iter = mLifecycleObservers.Begin(), endIter = mLifecycleObservers.End(); iter != endIter; ++iter)
122   {
123     (*iter)->TextureManagerDestroyed();
124   }
125 }
126
127 TextureSet TextureManager::LoadAnimatedImageTexture(
128   const VisualUrl&                url,
129   Dali::AnimatedImageLoading      animatedImageLoading,
130   const uint32_t                  frameIndex,
131   TextureManager::TextureId&      textureId,
132   MaskingDataPointer&             maskInfo,
133   const Dali::ImageDimensions&    desiredSize,
134   const Dali::FittingMode::Type   fittingMode,
135   const Dali::SamplingMode::Type  samplingMode,
136   const bool                      synchronousLoading,
137   TextureUploadObserver*          textureObserver,
138   TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
139 {
140   TextureSet textureSet;
141
142   if(synchronousLoading)
143   {
144     Devel::PixelBuffer pixelBuffer;
145     if(animatedImageLoading)
146     {
147       pixelBuffer = animatedImageLoading.LoadFrame(frameIndex, desiredSize, fittingMode, samplingMode);
148     }
149     if(!pixelBuffer)
150     {
151       DALI_LOG_ERROR("TextureManager::LoadAnimatedImageTexture: Synchronous loading is failed\n");
152     }
153     else
154     {
155       Texture maskTexture;
156       if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
157       {
158         Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile(maskInfo->mAlphaMaskUrl.GetUrl(), desiredSize, fittingMode, samplingMode, true);
159         if(maskPixelBuffer)
160         {
161           if(!maskInfo->mPreappliedMasking)
162           {
163             PixelData maskPixelData = Devel::PixelBuffer::Convert(maskPixelBuffer); // takes ownership of buffer
164             maskTexture             = Texture::New(Dali::TextureType::TEXTURE_2D, maskPixelData.GetPixelFormat(), maskPixelData.GetWidth(), maskPixelData.GetHeight());
165             maskTexture.Upload(maskPixelData);
166           }
167           else
168           {
169             pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
170           }
171         }
172         else
173         {
174           DALI_LOG_ERROR("TextureManager::LoadAnimatedImageTexture: Synchronous mask image loading is failed\n");
175         }
176       }
177
178       if(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD)
179       {
180         PreMultiply(pixelBuffer, preMultiplyOnLoad);
181       }
182
183       PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
184       if(!textureSet)
185       {
186         Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
187         texture.Upload(pixelData);
188         textureSet = TextureSet::New();
189         textureSet.SetTexture(TEXTURE_INDEX, texture);
190         if(maskTexture)
191         {
192           textureSet.SetTexture(MASK_TEXTURE_INDEX, maskTexture);
193         }
194       }
195     }
196   }
197   else
198   {
199     TextureId alphaMaskId        = INVALID_TEXTURE_ID;
200     float     contentScaleFactor = 1.0f;
201     bool      cropToMask         = false;
202     if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
203     {
204       maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, maskInfo->mPreappliedMasking ? TextureManager::StorageType::KEEP_PIXEL_BUFFER : TextureManager::StorageType::KEEP_TEXTURE);
205       alphaMaskId            = maskInfo->mAlphaMaskId;
206       if(maskInfo->mPreappliedMasking)
207       {
208         contentScaleFactor = maskInfo->mContentScaleFactor;
209         cropToMask         = maskInfo->mCropToMask;
210       }
211     }
212
213     textureId = RequestLoadInternal(url, alphaMaskId, textureId, contentScaleFactor, desiredSize, fittingMode, samplingMode, cropToMask, TextureManager::StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoad, animatedImageLoading, frameIndex, false);
214
215     TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId);
216     if(loadState == TextureManager::LoadState::UPLOADED)
217     {
218       // LoadComplete has already been called - keep the same texture set
219       textureSet = GetTextureSet(textureId);
220     }
221   }
222
223   return textureSet;
224 }
225
226 Devel::PixelBuffer TextureManager::LoadPixelBuffer(
227   const VisualUrl&                url,
228   const Dali::ImageDimensions&    desiredSize,
229   const Dali::FittingMode::Type   fittingMode,
230   const Dali::SamplingMode::Type  samplingMode,
231   const bool                      synchronousLoading,
232   TextureUploadObserver*          textureObserver,
233   const bool                      orientationCorrection,
234   TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
235 {
236   Devel::PixelBuffer pixelBuffer;
237   if(synchronousLoading)
238   {
239     if(url.IsValid())
240     {
241       if(url.IsBufferResource())
242       {
243         const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(url);
244         if(encodedImageBuffer)
245         {
246           pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
247         }
248       }
249       else
250       {
251         pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
252       }
253       if(pixelBuffer && preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD)
254       {
255         PreMultiply(pixelBuffer, preMultiplyOnLoad);
256       }
257     }
258   }
259   else
260   {
261     RequestLoadInternal(url, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, false, TextureManager::StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
262   }
263
264   return pixelBuffer;
265 }
266
267 TextureSet TextureManager::LoadTexture(
268   const VisualUrl&                   url,
269   const Dali::ImageDimensions&       desiredSize,
270   const Dali::FittingMode::Type      fittingMode,
271   const Dali::SamplingMode::Type     samplingMode,
272   MaskingDataPointer&                maskInfo,
273   const bool                         synchronousLoading,
274   TextureManager::TextureId&         textureId,
275   Vector4&                           textureRect,
276   Dali::ImageDimensions&             textureRectSize,
277   bool&                              atlasingStatus,
278   bool&                              loadingStatus,
279   TextureUploadObserver*             textureObserver,
280   AtlasUploadObserver*               atlasObserver,
281   ImageAtlasManagerPtr               imageAtlasManager,
282   const bool                         orientationCorrection,
283   const TextureManager::ReloadPolicy reloadPolicy,
284   TextureManager::MultiplyOnLoad&    preMultiplyOnLoad)
285 {
286   TextureSet textureSet;
287
288   loadingStatus = false;
289   textureRect   = FULL_ATLAS_RECT;
290
291   if(VisualUrl::TEXTURE == url.GetProtocolType())
292   {
293     std::string location = url.GetLocation();
294     if(location.size() > 0u)
295     {
296       TextureId id                  = std::stoi(location);
297       auto      externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id);
298       if(externalTextureInfo.textureSet)
299       {
300         if(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD)
301         {
302           // Change preMultiplyOnLoad value so make caller determine to preMultiplyAlpha or not.
303           // TODO : Should we seperate input and output value?
304           preMultiplyOnLoad = externalTextureInfo.preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
305         }
306
307         TextureId alphaMaskId = INVALID_TEXTURE_ID;
308         if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
309         {
310           maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, TextureManager::StorageType::KEEP_TEXTURE, synchronousLoading);
311           alphaMaskId            = maskInfo->mAlphaMaskId;
312
313           // Create new textureId. this textureId is not same as location
314           textureId = RequestLoad(url, alphaMaskId, textureId, 1.0f, desiredSize, fittingMode, samplingMode, false, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading);
315
316           TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId);
317           if(loadState == TextureManager::LoadState::UPLOADED)
318           {
319             textureSet = GetTextureSet(textureId);
320           }
321         }
322         else
323         {
324           // TextureId is same as location
325           textureId = id;
326
327           textureSet = TextureSet::New();
328           textureSet.SetTexture(TEXTURE_INDEX, externalTextureInfo.textureSet.GetTexture(TEXTURE_INDEX));
329         }
330       }
331     }
332   }
333   else
334   {
335     // For Atlas
336     if(synchronousLoading && atlasingStatus)
337     {
338       const bool synchronousAtlasAvaliable = (desiredSize != ImageDimensions() || url.IsLocalResource()) ? imageAtlasManager->CheckAtlasAvailable(url, desiredSize)
339                                                                                                          : false;
340       if(synchronousAtlasAvaliable)
341       {
342         std::vector<Devel::PixelBuffer> pixelBuffers;
343         LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, false, pixelBuffers);
344
345         if(!pixelBuffers.empty() && maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
346         {
347           std::vector<Devel::PixelBuffer> maskPixelBuffers;
348           LoadImageSynchronously(maskInfo->mAlphaMaskUrl, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true, false, maskPixelBuffers);
349           if(!maskPixelBuffers.empty())
350           {
351             pixelBuffers[0].ApplyMask(maskPixelBuffers[0], maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
352           }
353         }
354
355         PixelData data;
356         if(!pixelBuffers.empty())
357         {
358           PreMultiply(pixelBuffers[0], preMultiplyOnLoad);
359           data = Devel::PixelBuffer::Convert(pixelBuffers[0]); // takes ownership of buffer
360
361           if(data)
362           {
363             textureSet = imageAtlasManager->Add(textureRect, data);
364             if(textureSet)
365             {
366               textureRectSize.SetWidth(data.GetWidth());
367               textureRectSize.SetHeight(data.GetHeight());
368             }
369           }
370           else
371           {
372             DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous Texture loading with atlasing is failed.\n");
373           }
374         }
375         if(!textureSet)
376         {
377           atlasingStatus = false;
378         }
379       }
380     }
381
382     if(!textureSet)
383     {
384       loadingStatus = true;
385       // Atlas manager can chage desired size when it is set by 0,0.
386       // We should store into textureRectSize only if atlasing successed.
387       // So copy inputed desiredSize, and replace value into textureRectSize only if atlasing success.
388       Dali::ImageDimensions atlasDesiredSize = desiredSize;
389       if(atlasingStatus)
390       {
391         if(url.IsBufferResource())
392         {
393           const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl());
394           if(encodedImageBuffer)
395           {
396             textureSet = imageAtlasManager->Add(textureRect, encodedImageBuffer, desiredSize, fittingMode, true, atlasObserver);
397           }
398         }
399         else
400         {
401           textureSet = imageAtlasManager->Add(textureRect, url, atlasDesiredSize, fittingMode, true, atlasObserver);
402         }
403       }
404       if(!textureSet) // big image, no atlasing or atlasing failed
405       {
406         atlasingStatus = false;
407
408         TextureId alphaMaskId        = INVALID_TEXTURE_ID;
409         float     contentScaleFactor = 1.0f;
410         bool      cropToMask         = false;
411         if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
412         {
413           maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, maskInfo->mPreappliedMasking ? TextureManager::StorageType::KEEP_PIXEL_BUFFER : TextureManager::StorageType::KEEP_TEXTURE, synchronousLoading);
414           alphaMaskId            = maskInfo->mAlphaMaskId;
415           if(maskInfo && maskInfo->mPreappliedMasking)
416           {
417             contentScaleFactor = maskInfo->mContentScaleFactor;
418             cropToMask         = maskInfo->mCropToMask;
419           }
420         }
421
422         textureId = RequestLoad(
423           url,
424           alphaMaskId,
425           textureId,
426           contentScaleFactor,
427           desiredSize,
428           fittingMode,
429           samplingMode,
430           cropToMask,
431           textureObserver,
432           orientationCorrection,
433           reloadPolicy,
434           preMultiplyOnLoad,
435           synchronousLoading);
436
437         TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId);
438         if(loadState == TextureManager::LoadState::UPLOADED)
439         {
440           // LoadComplete has already been called - keep the same texture set
441           textureSet = GetTextureSet(textureId);
442         }
443
444         // If we are loading the texture, or waiting for the ready signal handler to complete, inform
445         // caller that they need to wait.
446         loadingStatus = (loadState == TextureManager::LoadState::LOADING ||
447                          loadState == TextureManager::LoadState::WAITING_FOR_MASK ||
448                          loadState == TextureManager::LoadState::MASK_APPLYING ||
449                          loadState == TextureManager::LoadState::MASK_APPLIED ||
450                          loadState == TextureManager::LoadState::NOT_STARTED ||
451                          mLoadingQueueTextureId != INVALID_TEXTURE_ID);
452       }
453       else
454       {
455         textureRectSize = atlasDesiredSize;
456       }
457     }
458   }
459
460   if(synchronousLoading)
461   {
462     loadingStatus = false;
463   }
464
465   return textureSet;
466 }
467
468 TextureManager::TextureId TextureManager::RequestLoad(
469   const VisualUrl&                   url,
470   const ImageDimensions&             desiredSize,
471   const Dali::FittingMode::Type      fittingMode,
472   const Dali::SamplingMode::Type     samplingMode,
473   TextureUploadObserver*             observer,
474   const bool                         orientationCorrection,
475   const TextureManager::ReloadPolicy reloadPolicy,
476   TextureManager::MultiplyOnLoad&    preMultiplyOnLoad,
477   const bool                         synchronousLoading)
478 {
479   return RequestLoadInternal(url, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, false, TextureManager::StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
480 }
481
482 TextureManager::TextureId TextureManager::RequestLoad(
483   const VisualUrl&                   url,
484   const TextureManager::TextureId    maskTextureId,
485   const TextureManager::TextureId    previousTextureId,
486   const float                        contentScale,
487   const Dali::ImageDimensions&       desiredSize,
488   const Dali::FittingMode::Type      fittingMode,
489   const Dali::SamplingMode::Type     samplingMode,
490   const bool                         cropToMask,
491   TextureUploadObserver*             observer,
492   const bool                         orientationCorrection,
493   const TextureManager::ReloadPolicy reloadPolicy,
494   TextureManager::MultiplyOnLoad&    preMultiplyOnLoad,
495   const bool                         synchronousLoading)
496 {
497   return RequestLoadInternal(url, maskTextureId, previousTextureId, contentScale, desiredSize, fittingMode, samplingMode, cropToMask, TextureManager::StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
498 }
499
500 TextureManager::TextureId TextureManager::RequestMaskLoad(
501   const VisualUrl&                  maskUrl,
502   const TextureManager::StorageType storageType,
503   const bool                        synchronousLoading)
504 {
505   // Use the normal load procedure to get the alpha mask.
506   auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
507   return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), Dali::FittingMode::SCALE_TO_FILL, Dali::SamplingMode::NO_FILTER, false, storageType, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
508 }
509
510 TextureManager::TextureId TextureManager::RequestLoadInternal(
511   const VisualUrl&                   url,
512   const TextureManager::TextureId    maskTextureId,
513   const TextureManager::TextureId    previousTextureId,
514   const float                        contentScale,
515   const Dali::ImageDimensions&       desiredSize,
516   const Dali::FittingMode::Type      fittingMode,
517   const Dali::SamplingMode::Type     samplingMode,
518   const bool                         cropToMask,
519   const TextureManager::StorageType  storageType,
520   TextureUploadObserver*             observer,
521   const bool                         orientationCorrection,
522   const TextureManager::ReloadPolicy reloadPolicy,
523   TextureManager::MultiplyOnLoad&    preMultiplyOnLoad,
524   Dali::AnimatedImageLoading         animatedImageLoading,
525   const uint32_t                     frameIndex,
526   const bool                         synchronousLoading)
527 {
528   TextureHash       textureHash   = INITIAL_HASH_NUMBER;
529   TextureCacheIndex cacheIndex    = INVALID_CACHE_INDEX;
530   bool              loadYuvPlanes = (mLoadYuvPlanes && maskTextureId == INVALID_TEXTURE_ID && storageType == TextureManager::StorageType::UPLOAD_TO_TEXTURE);
531
532   if(storageType != TextureManager::StorageType::RETURN_PIXEL_BUFFER)
533   {
534     textureHash = mTextureCacheManager.GenerateHash(url, desiredSize, fittingMode, samplingMode, maskTextureId, cropToMask, frameIndex);
535
536     // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
537     cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url, desiredSize, fittingMode, samplingMode, storageType, maskTextureId, cropToMask, preMultiplyOnLoad, (animatedImageLoading) ? true : false, frameIndex);
538   }
539
540   TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
541   // Check if the requested Texture exists in the cache.
542   if(cacheIndex != INVALID_CACHE_INDEX)
543   {
544     if(TextureManager::ReloadPolicy::CACHED == reloadPolicy || TextureManager::INVALID_TEXTURE_ID == previousTextureId)
545     {
546       // Mark this texture being used by another client resource, or Reload forced without request load before.
547       // Forced reload which have current texture before, would replace the current texture.
548       // without the need for incrementing the reference count.
549       ++(mTextureCacheManager[cacheIndex].referenceCount);
550     }
551     textureId = mTextureCacheManager[cacheIndex].textureId;
552
553     // Update preMultiplyOnLoad value. It should be changed according to preMultiplied value of the cached info.
554     preMultiplyOnLoad = mTextureCacheManager[cacheIndex].preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
555     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d, maskTextureId=%d, prevTextureId=%d, frameindex=%d, premultiplied=%d, refCount=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId, frameIndex, mTextureCacheManager[cacheIndex].preMultiplied ? 1 : 0, static_cast<int>(mTextureCacheManager[cacheIndex].referenceCount));
556   }
557
558   if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
559   {
560     textureId = mTextureCacheManager.GenerateTextureId();
561
562     bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
563
564     // Cache new texutre, and get cacheIndex.
565     cacheIndex = mTextureCacheManager.AppendCache(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex, loadYuvPlanes));
566     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d, maskTextureId=%d, frameindex=%d premultiply=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, frameIndex, preMultiply);
567   }
568
569   // The below code path is common whether we are using the cache or not.
570   // The textureInfoIndex now refers to either a pre-existing cached TextureInfo,
571   // or a new TextureInfo just created.
572   TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
573   textureInfo.maskTextureId         = maskTextureId;
574   textureInfo.storageType           = storageType;
575   textureInfo.orientationCorrection = orientationCorrection;
576
577   // the case using external texture has already been loaded texture, so change its status to WAITING_FOR_MASK.
578   if(url.GetProtocolType() == VisualUrl::TEXTURE)
579   {
580     if(textureInfo.loadState != TextureManager::LoadState::UPLOADED)
581     {
582       textureInfo.preMultiplied = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
583       textureInfo.loadState     = TextureManager::LoadState::WAITING_FOR_MASK;
584     }
585   }
586
587   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureInfo loadState:%s\n", GET_LOAD_STATE_STRING(textureInfo.loadState));
588
589   // Force reloading of texture by setting loadState unless already loading or cancelled.
590   if(TextureManager::ReloadPolicy::FORCED == reloadPolicy &&
591      TextureManager::LoadState::LOADING != textureInfo.loadState &&
592      TextureManager::LoadState::WAITING_FOR_MASK != textureInfo.loadState &&
593      TextureManager::LoadState::MASK_APPLYING != textureInfo.loadState &&
594      TextureManager::LoadState::MASK_APPLIED != textureInfo.loadState &&
595      TextureManager::LoadState::CANCELLED != textureInfo.loadState &&
596      TextureManager::LoadState::MASK_CANCELLED != textureInfo.loadState)
597   {
598     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d, maskTextureId=%d, prevTextureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId);
599     textureInfo.loadState = TextureManager::LoadState::NOT_STARTED;
600   }
601
602   if(!synchronousLoading)
603   {
604     // Check if we should add the observer.
605     // Only do this if we have not loaded yet and it will not have loaded by the end of this method.
606     switch(textureInfo.loadState)
607     {
608       case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing.
609       case TextureManager::LoadState::NOT_STARTED:
610       {
611         LoadOrQueueTexture(textureInfo, observer); // If called inside NotifyObservers, queues until afterwards
612         break;
613       }
614       case TextureManager::LoadState::LOADING:
615       case TextureManager::LoadState::WAITING_FOR_MASK:
616       case TextureManager::LoadState::MASK_APPLYING:
617       case TextureManager::LoadState::MASK_APPLIED:
618       {
619         // Do not observe even we reload forced when texture is already loading state.
620         if(TextureManager::ReloadPolicy::CACHED == reloadPolicy || TextureManager::INVALID_TEXTURE_ID == previousTextureId)
621         {
622           ObserveTexture(textureInfo, observer);
623         }
624         break;
625       }
626       case TextureManager::LoadState::UPLOADED:
627       {
628         if(observer)
629         {
630           LoadOrQueueTexture(textureInfo, observer);
631         }
632         break;
633       }
634       case TextureManager::LoadState::CANCELLED:
635       {
636         // A cancelled texture hasn't finished loading yet. Treat as a loading texture
637         // (it's ref count has already been incremented, above)
638         textureInfo.loadState = TextureManager::LoadState::LOADING;
639         ObserveTexture(textureInfo, observer);
640         break;
641       }
642       case TextureManager::LoadState::MASK_CANCELLED:
643       {
644         // A cancelled texture hasn't finished mask applying yet. Treat as a mask applying texture
645         // (it's ref count has already been incremented, above)
646         textureInfo.loadState = TextureManager::LoadState::MASK_APPLYING;
647         ObserveTexture(textureInfo, observer);
648         break;
649       }
650       case TextureManager::LoadState::LOAD_FINISHED:
651       {
652         // Loading has already completed.
653         if(observer && textureInfo.storageType == TextureManager::StorageType::RETURN_PIXEL_BUFFER)
654         {
655           LoadOrQueueTexture(textureInfo, observer);
656         }
657         break;
658       }
659     }
660   }
661   else
662   {
663     // If the image is already finished to load, use cached texture.
664     // We don't need to consider Observer because this is synchronous loading.
665     if(!(textureInfo.loadState == TextureManager::LoadState::UPLOADED ||
666          textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED))
667     {
668       if(url.GetProtocolType() == VisualUrl::TEXTURE)
669       {
670         // Get external textureSet from cacheManager.
671         std::string location = textureInfo.url.GetLocation();
672         if(!location.empty())
673         {
674           TextureId id                  = std::stoi(location);
675           auto      externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id);
676           textureInfo.textures.push_back(externalTextureInfo.textureSet.GetTexture(0));
677           textureInfo.loadState = TextureManager::LoadState::UPLOADED;
678         }
679       }
680       else
681       {
682         std::vector<Devel::PixelBuffer> pixelBuffers;
683         LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, loadYuvPlanes, pixelBuffers);
684
685         if(pixelBuffers.empty())
686         {
687           // If pixelBuffer loading is failed in synchronously, call RequestRemove() method.
688           RequestRemove(textureId, nullptr);
689           return INVALID_TEXTURE_ID;
690         }
691
692         if(storageType == TextureManager::StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading.
693         {
694           textureInfo.pixelBuffer = pixelBuffers[0]; // Store the pixel data
695           textureInfo.loadState   = TextureManager::LoadState::LOAD_FINISHED;
696         }
697         else // For the image loading.
698         {
699           Texture maskTexture;
700           if(maskTextureId != INVALID_TEXTURE_ID)
701           {
702             TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId);
703             if(maskCacheIndex != INVALID_CACHE_INDEX)
704             {
705               if(mTextureCacheManager[maskCacheIndex].storageType == TextureManager::StorageType::KEEP_PIXEL_BUFFER)
706               {
707                 Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer;
708                 if(maskPixelBuffer)
709                 {
710                   pixelBuffers[0].ApplyMask(maskPixelBuffer, contentScale, cropToMask);
711                 }
712                 else
713                 {
714                   DALI_LOG_ERROR("Mask image cached invalid pixel buffer!\n");
715                 }
716               }
717             }
718             else
719             {
720               DALI_LOG_ERROR("Mask image is not stored in cache.\n");
721             }
722           }
723           PreMultiply(pixelBuffers[0], preMultiplyOnLoad);
724
725           // Upload texture
726           UploadTextures(pixelBuffers, textureInfo);
727         }
728       }
729     }
730   }
731
732   return textureId;
733 }
734
735 void TextureManager::RequestRemove(const TextureManager::TextureId textureId, TextureUploadObserver* observer)
736 {
737   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestRemove( textureId=%d observer=%p )\n", textureId, observer);
738
739   // Queue to remove.
740   if(textureId != INVALID_TEXTURE_ID)
741   {
742     TextureCacheIndex textureCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId);
743     if(textureCacheIndex != INVALID_CACHE_INDEX)
744     {
745       if(observer)
746       {
747         // Remove observer from cached texture info
748         TextureInfo& textureInfo(mTextureCacheManager[textureCacheIndex]);
749         RemoveTextureObserver(textureInfo, observer);
750       }
751
752       mRemoveQueue.PushBack(textureId);
753
754       if(!mRemoveProcessorRegistered && Adaptor::IsAvailable())
755       {
756         mRemoveProcessorRegistered = true;
757         Adaptor::Get().RegisterProcessor(*this, true);
758       }
759     }
760   }
761 }
762
763 void TextureManager::Remove(const TextureManager::TextureId textureId)
764 {
765   if(textureId != INVALID_TEXTURE_ID)
766   {
767     TextureCacheIndex textureCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId);
768     if(textureCacheIndex != INVALID_CACHE_INDEX)
769     {
770       TextureManager::TextureId maskTextureId = INVALID_TEXTURE_ID;
771       TextureInfo&              textureInfo(mTextureCacheManager[textureCacheIndex]);
772       // We only need to consider maskTextureId when texture's loadState is not cancelled. Because it is already deleted.
773       if(textureInfo.loadState != TextureManager::LoadState::CANCELLED && textureInfo.loadState != TextureManager::LoadState::MASK_CANCELLED)
774       {
775         if(textureInfo.maskTextureId != INVALID_TEXTURE_ID)
776         {
777           maskTextureId = textureInfo.maskTextureId;
778         }
779       }
780
781       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Remove( textureId=%d ) cacheIndex:%d removal maskTextureId=%d, loadState=%s\n", textureId, textureCacheIndex.GetIndex(), maskTextureId, GET_LOAD_STATE_STRING(textureInfo.loadState));
782
783       // Remove textureId in CacheManager. Now, textureInfo is invalidate.
784       mTextureCacheManager.RemoveCache(textureInfo);
785
786       // Remove maskTextureId in CacheManager
787       if(maskTextureId != INVALID_TEXTURE_ID)
788       {
789         TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId);
790         if(maskCacheIndex != INVALID_CACHE_INDEX)
791         {
792           TextureInfo& maskTextureInfo(mTextureCacheManager[maskCacheIndex]);
793
794           DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Remove mask texture( maskTextureId=%d ) cacheIndex:%d, loadState=%s\n", maskTextureId, maskCacheIndex.GetIndex(), GET_LOAD_STATE_STRING(maskTextureInfo.loadState));
795
796           mTextureCacheManager.RemoveCache(maskTextureInfo);
797         }
798       }
799     }
800   }
801 }
802
803 void TextureManager::ProcessRemoveQueue()
804 {
805   // Note that RemoveQueue is not be changed during Remove().
806   for(auto&& textureId : mRemoveQueue)
807   {
808     if(textureId != INVALID_TEXTURE_ID)
809     {
810       Remove(textureId);
811     }
812   }
813   mRemoveQueue.Clear();
814 }
815
816 void TextureManager::Process(bool postProcessor)
817 {
818   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Process()\n");
819
820   ProcessRemoveQueue();
821
822   if(Adaptor::IsAvailable())
823   {
824     Adaptor::Get().UnregisterProcessor(*this, true);
825     mRemoveProcessorRegistered = false;
826   }
827 }
828
829 void TextureManager::LoadImageSynchronously(
830   const VisualUrl&                 url,
831   const Dali::ImageDimensions&     desiredSize,
832   const Dali::FittingMode::Type    fittingMode,
833   const Dali::SamplingMode::Type   samplingMode,
834   const bool                       orientationCorrection,
835   const bool                       loadYuvPlanes,
836   std::vector<Devel::PixelBuffer>& pixelBuffers)
837 {
838   Devel::PixelBuffer pixelBuffer;
839   if(url.IsBufferResource())
840   {
841     const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(url);
842     if(encodedImageBuffer)
843     {
844       pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
845     }
846   }
847   else
848   {
849     if(loadYuvPlanes)
850     {
851       Dali::LoadImagePlanesFromFile(url.GetUrl(), pixelBuffers, desiredSize, fittingMode, samplingMode, orientationCorrection);
852     }
853     else
854     {
855       pixelBuffer = Dali::LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
856     }
857   }
858
859   if(pixelBuffer)
860   {
861     pixelBuffers.push_back(pixelBuffer);
862   }
863 }
864
865 void TextureManager::AddObserver(TextureManager::LifecycleObserver& observer)
866 {
867   // make sure an observer doesn't observe the same object twice
868   // otherwise it will get multiple calls to ObjectDestroyed()
869   DALI_ASSERT_DEBUG(mLifecycleObservers.End() == std::find(mLifecycleObservers.Begin(), mLifecycleObservers.End(), &observer));
870   mLifecycleObservers.PushBack(&observer);
871 }
872
873 void TextureManager::RemoveObserver(TextureManager::LifecycleObserver& observer)
874 {
875   // Find the observer...
876   auto endIter = mLifecycleObservers.End();
877   for(auto iter = mLifecycleObservers.Begin(); iter != endIter; ++iter)
878   {
879     if((*iter) == &observer)
880     {
881       mLifecycleObservers.Erase(iter);
882       break;
883     }
884   }
885   DALI_ASSERT_DEBUG(endIter != mLifecycleObservers.End());
886 }
887
888 void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer)
889 {
890   switch(textureInfo.loadState)
891   {
892     case TextureManager::LoadState::NOT_STARTED:
893     case TextureManager::LoadState::LOAD_FAILED:
894     {
895       if(mLoadingQueueTextureId != INVALID_TEXTURE_ID)
896       {
897         QueueLoadTexture(textureInfo, observer);
898       }
899       else
900       {
901         LoadTexture(textureInfo, observer);
902       }
903       break;
904     }
905     case TextureManager::LoadState::UPLOADED:
906     {
907       if(mLoadingQueueTextureId != INVALID_TEXTURE_ID)
908       {
909         QueueLoadTexture(textureInfo, observer);
910       }
911       else
912       {
913         // The Texture has already loaded. The other observers have already been notified.
914         // We need to send a "late" loaded notification for this observer.
915         if(observer)
916         {
917           EmitLoadComplete(observer, textureInfo, true);
918         }
919       }
920       break;
921     }
922     case TextureManager::LoadState::LOADING:
923     case TextureManager::LoadState::CANCELLED:
924     case TextureManager::LoadState::MASK_CANCELLED:
925     case TextureManager::LoadState::LOAD_FINISHED:
926     case TextureManager::LoadState::WAITING_FOR_MASK:
927     case TextureManager::LoadState::MASK_APPLYING:
928     case TextureManager::LoadState::MASK_APPLIED:
929     {
930       break;
931     }
932   }
933 }
934
935 void TextureManager::QueueLoadTexture(const TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer)
936 {
937   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "Add observer to observer queue (textureId:%d, observer:%p)\n", textureInfo.textureId, observer);
938
939   const auto& textureId = textureInfo.textureId;
940   mLoadQueue.PushBack(QueueElement(textureId, observer));
941
942   if(observer)
943   {
944     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Connect DestructionSignal to observer:%p\n", observer);
945     observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed);
946   }
947 }
948
949 void TextureManager::LoadTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer)
950 {
951   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::LoadTexture(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F");
952   textureInfo.loadState = TextureManager::LoadState::LOADING;
953   if(!textureInfo.loadSynchronously)
954   {
955     auto premultiplyOnLoad = (textureInfo.preMultiplyOnLoad && textureInfo.maskTextureId == INVALID_TEXTURE_ID) ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF;
956     if(textureInfo.animatedImageLoading)
957     {
958       mAsyncLoader->LoadAnimatedImage(textureInfo.textureId, textureInfo.animatedImageLoading, textureInfo.frameIndex, textureInfo.desiredSize, textureInfo.fittingMode, textureInfo.samplingMode, premultiplyOnLoad);
959     }
960     else
961     {
962       mAsyncLoader->Load(textureInfo.textureId, textureInfo.url, textureInfo.desiredSize, textureInfo.fittingMode, textureInfo.samplingMode, textureInfo.orientationCorrection, premultiplyOnLoad, textureInfo.loadYuvPlanes);
963     }
964   }
965   ObserveTexture(textureInfo, observer);
966 }
967
968 void TextureManager::ProcessLoadQueue()
969 {
970   for(auto&& element : mLoadQueue)
971   {
972     if(element.mTextureId == INVALID_TEXTURE_ID)
973     {
974       continue;
975     }
976
977     TextureCacheIndex cacheIndex = mTextureCacheManager.GetCacheIndexFromId(element.mTextureId);
978     if(cacheIndex != INVALID_CACHE_INDEX)
979     {
980       TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
981
982       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::ProcessLoadQueue() textureId=%d, observer=%p, cacheIndex=@%d, loadState:%s\n", element.mTextureId, element.mObserver, cacheIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState));
983
984       if((textureInfo.loadState == TextureManager::LoadState::UPLOADED) ||
985          (textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED &&
986           textureInfo.storageType == TextureManager::StorageType::RETURN_PIXEL_BUFFER))
987       {
988         if(element.mObserver)
989         {
990           EmitLoadComplete(element.mObserver, textureInfo, true);
991         }
992       }
993       else if(textureInfo.loadState == TextureManager::LoadState::LOADING)
994       {
995         // Note : LOADING state texture cannot be queue.
996         // This case be occured when same texture id are queue in mLoadQueue.
997         ObserveTexture(textureInfo, element.mObserver);
998       }
999       else
1000       {
1001         LoadTexture(textureInfo, element.mObserver);
1002       }
1003     }
1004   }
1005   mLoadQueue.Clear();
1006 }
1007
1008 void TextureManager::ObserveTexture(TextureManager::TextureInfo& textureInfo,
1009                                     TextureUploadObserver*       observer)
1010 {
1011   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ObserveTexture(): url:%s observer:%p\n", textureInfo.url.GetUrl().c_str(), observer);
1012
1013   if(observer)
1014   {
1015     textureInfo.observerList.PushBack(observer);
1016
1017     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Connect DestructionSignal to observer:%p\n", observer);
1018     observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed);
1019   }
1020 }
1021
1022 void TextureManager::AsyncLoadComplete(const TextureManager::TextureId textureId, std::vector<Devel::PixelBuffer>& pixelBuffers)
1023 {
1024   TextureCacheIndex cacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId);
1025   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( textureId:%d CacheIndex:%d )\n", textureId, cacheIndex.GetIndex());
1026   if(cacheIndex != INVALID_CACHE_INDEX)
1027   {
1028     TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
1029
1030     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));
1031     if(textureInfo.loadState != TextureManager::LoadState::CANCELLED && textureInfo.loadState != TextureManager::LoadState::MASK_CANCELLED)
1032     {
1033       // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified)
1034       PostLoad(textureInfo, pixelBuffers);
1035     }
1036     else
1037     {
1038       Remove(textureInfo.textureId);
1039     }
1040   }
1041 }
1042
1043 void TextureManager::PostLoad(TextureManager::TextureInfo& textureInfo, std::vector<Devel::PixelBuffer>& pixelBuffers)
1044 {
1045   if(!pixelBuffers.empty()) ///< Load success
1046   {
1047     if(pixelBuffers.size() == 1)
1048     {
1049       Devel::PixelBuffer pixelBuffer = pixelBuffers[0];
1050       if(pixelBuffer && (pixelBuffer.GetWidth() != 0) && (pixelBuffer.GetHeight() != 0))
1051       {
1052         textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied();
1053
1054         if(textureInfo.storageType == TextureManager::StorageType::UPLOAD_TO_TEXTURE)
1055         {
1056           // If there is a mask texture ID associated with this texture, then apply the mask
1057           // if it's already loaded. If it hasn't, and the mask is still loading,
1058           // wait for the mask to finish loading.
1059           // note, If the texture is already uploaded synchronously during loading,
1060           // we don't need to apply mask.
1061           if(textureInfo.loadState != TextureManager::LoadState::UPLOADED &&
1062              textureInfo.maskTextureId != INVALID_TEXTURE_ID)
1063           {
1064             if(textureInfo.loadState == TextureManager::LoadState::MASK_APPLYING)
1065             {
1066               textureInfo.loadState = TextureManager::LoadState::MASK_APPLIED;
1067               UploadTextures(pixelBuffers, textureInfo);
1068               NotifyObservers(textureInfo, true);
1069             }
1070             else
1071             {
1072               LoadState maskLoadState = mTextureCacheManager.GetTextureStateInternal(textureInfo.maskTextureId);
1073               textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily
1074               if(maskLoadState == TextureManager::LoadState::LOADING)
1075               {
1076                 textureInfo.loadState = TextureManager::LoadState::WAITING_FOR_MASK;
1077               }
1078               else if(maskLoadState == TextureManager::LoadState::LOAD_FINISHED || maskLoadState == TextureManager::LoadState::UPLOADED)
1079               {
1080                 TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureInfo.maskTextureId);
1081                 if(maskCacheIndex != INVALID_CACHE_INDEX)
1082                 {
1083                   TextureInfo& maskTextureInfo(mTextureCacheManager[maskCacheIndex]);
1084                   if(maskTextureInfo.storageType == TextureManager::StorageType::KEEP_PIXEL_BUFFER)
1085                   {
1086                     ApplyMask(textureInfo, textureInfo.maskTextureId);
1087                   }
1088                   else if(maskTextureInfo.storageType == TextureManager::StorageType::KEEP_TEXTURE)
1089                   {
1090                     // Upload image texture. textureInfo.loadState will be UPLOADED.
1091                     UploadTextures(pixelBuffers, textureInfo);
1092
1093                     // notify mask texture set.
1094                     NotifyObservers(textureInfo, true);
1095                   }
1096                 }
1097               }
1098               else // maskLoadState == TextureManager::LoadState::LOAD_FAILED
1099               {
1100                 // Url texture load success, But alpha mask texture load failed. Run as normal image upload.
1101                 DALI_LOG_ERROR("Alpha mask image loading failed! Image will not be masked\n");
1102                 UploadTextures(pixelBuffers, textureInfo);
1103                 NotifyObservers(textureInfo, true);
1104               }
1105             }
1106           }
1107           else
1108           {
1109             UploadTextures(pixelBuffers, textureInfo);
1110             NotifyObservers(textureInfo, true);
1111           }
1112         }
1113         else
1114         {
1115           textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
1116           textureInfo.loadState   = TextureManager::LoadState::LOAD_FINISHED;
1117
1118           if(textureInfo.storageType == TextureManager::StorageType::RETURN_PIXEL_BUFFER)
1119           {
1120             NotifyObservers(textureInfo, true);
1121           }
1122           else // for the TextureManager::StorageType::KEEP_PIXEL_BUFFER and TextureManager::StorageType::KEEP_TEXTURE
1123           {
1124             // Check if there was another texture waiting for this load to complete
1125             // (e.g. if this was an image mask, and its load is on a different thread)
1126             CheckForWaitingTexture(textureInfo);
1127           }
1128         }
1129       }
1130     }
1131     else
1132     {
1133       // YUV case
1134       textureInfo.preMultiplied = false;
1135
1136       UploadTextures(pixelBuffers, textureInfo);
1137       NotifyObservers(textureInfo, true);
1138     }
1139   }
1140   else ///< Load fail
1141   {
1142     textureInfo.loadState = TextureManager::LoadState::LOAD_FAILED;
1143     if(textureInfo.storageType == TextureManager::StorageType::KEEP_PIXEL_BUFFER || textureInfo.storageType == TextureManager::StorageType::KEEP_TEXTURE)
1144     {
1145       // Check if there was another texture waiting for this load to complete
1146       // (e.g. if this was an image mask, and its load is on a different thread)
1147       CheckForWaitingTexture(textureInfo);
1148     }
1149     else
1150     {
1151       NotifyObservers(textureInfo, false);
1152     }
1153   }
1154 }
1155
1156 void TextureManager::CheckForWaitingTexture(TextureManager::TextureInfo& maskTextureInfo)
1157 {
1158   if(maskTextureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED &&
1159      maskTextureInfo.storageType == TextureManager::StorageType::KEEP_TEXTURE)
1160   {
1161     // Upload mask texture. textureInfo.loadState will be UPLOADED.
1162     std::vector<Devel::PixelBuffer> pixelBuffers;
1163     pixelBuffers.push_back(maskTextureInfo.pixelBuffer);
1164     UploadTextures(pixelBuffers, maskTextureInfo);
1165   }
1166
1167   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::CheckForWaitingTexture(): maskTextureId=%d, maskTextureUrl=%s\n", maskTextureInfo.textureId, maskTextureInfo.url.GetUrl().c_str());
1168
1169   // Search the cache, checking if any texture has this texture id as a maskTextureId
1170   const size_t size = mTextureCacheManager.size();
1171
1172   // Keep notify observer required textureIds.
1173   // Note : NotifyObservers can change mTextureCacheManager cache struct. We should check id's validation before notify.
1174   std::vector<TextureId> notifyRequiredTextureIds;
1175
1176   // TODO : Refactorize here to not iterate whole cached image.
1177   for(TextureCacheIndex cacheIndex = TextureCacheIndex(TextureManagerType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, 0u); cacheIndex.GetIndex() < size; ++cacheIndex.detailValue.index)
1178   {
1179     if(mTextureCacheManager[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
1180        mTextureCacheManager[cacheIndex].loadState == TextureManager::LoadState::WAITING_FOR_MASK)
1181     {
1182       TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
1183
1184       if(maskTextureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED)
1185       {
1186         if(maskTextureInfo.storageType == TextureManager::StorageType::KEEP_PIXEL_BUFFER)
1187         {
1188           ApplyMask(textureInfo, maskTextureInfo.textureId);
1189         }
1190       }
1191       else if(maskTextureInfo.loadState == TextureManager::LoadState::UPLOADED)
1192       {
1193         if(maskTextureInfo.storageType == TextureManager::StorageType::KEEP_TEXTURE)
1194         {
1195           if(textureInfo.url.GetProtocolType() == VisualUrl::TEXTURE)
1196           {
1197             // Get external textureSet from cacheManager.
1198             std::string location = textureInfo.url.GetLocation();
1199             if(!location.empty())
1200             {
1201               TextureId id                  = std::stoi(location);
1202               auto      externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id);
1203               textureInfo.textures.push_back(externalTextureInfo.textureSet.GetTexture(0));
1204               textureInfo.loadState = TextureManager::LoadState::UPLOADED;
1205             }
1206           }
1207           else
1208           {
1209             // Upload image texture. textureInfo.loadState will be UPLOADED.
1210             std::vector<Devel::PixelBuffer> pixelBuffers;
1211             pixelBuffers.push_back(textureInfo.pixelBuffer);
1212             UploadTextures(pixelBuffers, textureInfo);
1213           }
1214
1215           // Increase reference counts for notify required textureId.
1216           // Now we can assume that we don't remove & re-assign this textureId
1217           // during NotifyObserver signal emit.
1218           maskTextureInfo.referenceCount++;
1219           textureInfo.referenceCount++;
1220
1221           DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::CheckForWaitingTexture(): Ready to notify textureId=%d\n", textureInfo.textureId);
1222
1223           notifyRequiredTextureIds.push_back(textureInfo.textureId);
1224         }
1225       }
1226       else // maskTextureInfo.loadState == TextureManager::LoadState::LOAD_FAILED
1227       {
1228         // Url texture load success, But alpha mask texture load failed. Run as normal image upload.
1229         DALI_LOG_ERROR("Alpha mask image loading failed! Image will not be masked\n");
1230         std::vector<Devel::PixelBuffer> pixelBuffers;
1231         pixelBuffers.push_back(textureInfo.pixelBuffer);
1232         UploadTextures(pixelBuffers, textureInfo);
1233
1234         // Increase reference counts for notify required textureId.
1235         // Now we can assume that we don't remove & re-assign this textureId
1236         // during NotifyObserver signal emit.
1237         maskTextureInfo.referenceCount++;
1238         textureInfo.referenceCount++;
1239
1240         DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::CheckForWaitingTexture(): Ready to notify textureId=%d\n", textureInfo.textureId);
1241
1242         notifyRequiredTextureIds.push_back(textureInfo.textureId);
1243       }
1244     }
1245   }
1246
1247   // Notify textures are masked
1248   for(const auto textureId : notifyRequiredTextureIds)
1249   {
1250     TextureCacheIndex textureCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId);
1251     if(textureCacheIndex != INVALID_CACHE_INDEX)
1252     {
1253       TextureInfo& textureInfo(mTextureCacheManager[textureCacheIndex]);
1254       NotifyObservers(textureInfo, true);
1255     }
1256   }
1257
1258   // Decrease reference count
1259   for(const auto textureId : notifyRequiredTextureIds)
1260   {
1261     RequestRemove(textureId, nullptr);
1262   }
1263 }
1264
1265 void TextureManager::ApplyMask(TextureManager::TextureInfo& textureInfo, const TextureManager::TextureId maskTextureId)
1266 {
1267   TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId);
1268   if(maskCacheIndex != INVALID_CACHE_INDEX)
1269   {
1270     Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer;
1271     Devel::PixelBuffer pixelBuffer     = textureInfo.pixelBuffer;
1272     textureInfo.pixelBuffer.Reset();
1273
1274     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ApplyMask(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F");
1275
1276     textureInfo.loadState  = TextureManager::LoadState::MASK_APPLYING;
1277     auto premultiplyOnLoad = textureInfo.preMultiplyOnLoad ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF;
1278     mAsyncLoader->ApplyMask(textureInfo.textureId, pixelBuffer, maskPixelBuffer, textureInfo.scaleFactor, textureInfo.cropToMask, premultiplyOnLoad);
1279   }
1280 }
1281
1282 void TextureManager::UploadTextures(std::vector<Devel::PixelBuffer>& pixelBuffers, TextureManager::TextureInfo& textureInfo)
1283 {
1284   if(!pixelBuffers.empty() && textureInfo.loadState != TextureManager::LoadState::UPLOADED)
1285   {
1286     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "  TextureManager::UploadTextures() New Texture for textureId:%d\n", textureInfo.textureId);
1287
1288     // Check if this pixelBuffer is premultiplied
1289     textureInfo.preMultiplied = pixelBuffers[0].IsAlphaPreMultiplied();
1290
1291     auto& renderingAddOn = RenderingAddOn::Get();
1292     if(renderingAddOn.IsValid())
1293     {
1294       renderingAddOn.CreateGeometry(textureInfo.textureId, pixelBuffers[0]);
1295     }
1296
1297     // Remove previous textures and insert new textures
1298     textureInfo.textures.clear();
1299
1300     for(auto&& pixelBuffer : pixelBuffers)
1301     {
1302       Texture   texture   = Texture::New(Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight());
1303       PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer);
1304       texture.Upload(pixelData);
1305       textureInfo.textures.push_back(texture);
1306     }
1307   }
1308
1309   // Update the load state.
1310   // Note: This is regardless of success as we care about whether a
1311   // load attempt is in progress or not.  If unsuccessful, a broken
1312   // image is still loaded.
1313   textureInfo.loadState = TextureManager::LoadState::UPLOADED;
1314 }
1315
1316 void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, const bool success)
1317 {
1318   TextureId textureId = textureInfo.textureId;
1319
1320   // If there is an observer: Notify the load is complete, whether successful or not,
1321   // and erase it from the list
1322   TextureInfo* info = &textureInfo;
1323
1324   if(info->animatedImageLoading)
1325   {
1326     // If loading failed, we don't need to get frameCount and frameInterval.
1327     if(success)
1328     {
1329       info->frameCount    = info->animatedImageLoading.GetImageCount();
1330       info->frameInterval = info->animatedImageLoading.GetFrameInterval(info->frameIndex);
1331     }
1332     info->animatedImageLoading.Reset();
1333   }
1334
1335   mLoadingQueueTextureId = textureId;
1336
1337   // Reverse observer list that we can pop_back the observer.
1338   std::reverse(info->observerList.Begin(), info->observerList.End());
1339
1340   while(info->observerList.Count())
1341   {
1342     TextureUploadObserver* observer = *(info->observerList.End() - 1u);
1343
1344     // During LoadComplete() a Control ResourceReady() signal is emitted.
1345     // During that signal the app may add remove /add Textures (e.g. via
1346     // ImageViews).
1347     // It is possible for observers to be removed from the observer list,
1348     // and it is also possible for the mTextureInfoContainer to be modified,
1349     // invalidating the reference to the textureInfo struct.
1350     // Texture load requests for the same URL are deferred until the end of this
1351     // method.
1352     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::NotifyObservers() observer:%p textureId:%d url:%s loadState:%s\n", observer, textureId, info->url.GetUrl().c_str(), GET_LOAD_STATE_STRING(info->loadState));
1353     // It is possible for the observer to be deleted.
1354     // Disconnect and remove the observer first.
1355     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Disconnect DestructionSignal to observer:%p\n", observer);
1356     observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed);
1357
1358     info->observerList.Erase(info->observerList.End() - 1u);
1359
1360     EmitLoadComplete(observer, *info, success);
1361
1362     // Get the textureInfo from the container again as it may have been invalidated.
1363     TextureCacheIndex textureInfoIndex = mTextureCacheManager.GetCacheIndexFromId(textureId);
1364     if(textureInfoIndex == INVALID_CACHE_INDEX)
1365     {
1366       break; // texture has been removed - can stop.
1367     }
1368     info = &mTextureCacheManager[textureInfoIndex];
1369   }
1370
1371   mLoadingQueueTextureId = INVALID_TEXTURE_ID;
1372   ProcessLoadQueue();
1373
1374   if(info->storageType == TextureManager::StorageType::RETURN_PIXEL_BUFFER && info->observerList.Count() == 0)
1375   {
1376     RequestRemove(info->textureId, nullptr);
1377   }
1378 }
1379
1380 void TextureManager::ObserverDestroyed(TextureUploadObserver* observer)
1381 {
1382   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::ObserverDestroyed() observer:%p\n", observer);
1383
1384   const std::size_t size = mTextureCacheManager.size();
1385   for(TextureCacheIndex cacheIndex = TextureCacheIndex(TextureManagerType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, 0u); cacheIndex.GetIndex() < size; ++cacheIndex.detailValue.index)
1386   {
1387     TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
1388     for(TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin();
1389         j != textureInfo.observerList.End();)
1390     {
1391       if(*j == observer)
1392       {
1393         j = textureInfo.observerList.Erase(j);
1394       }
1395       else
1396       {
1397         ++j;
1398       }
1399     }
1400   }
1401
1402   // Remove element from the LoadQueue
1403   for(auto&& element : mLoadQueue)
1404   {
1405     if(element.mObserver == observer)
1406     {
1407       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "Remove observer from observer queue (textureId:%d, observer:%p)\n", element.mTextureId, element.mObserver);
1408       element.mTextureId = INVALID_TEXTURE_ID;
1409       element.mObserver  = nullptr;
1410     }
1411   }
1412 }
1413
1414 Dali::Geometry TextureManager::GetRenderGeometry(const TextureManager::TextureId textureId, uint32_t& frontElements, uint32_t& backElements)
1415 {
1416   return RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().GetGeometry(textureId, frontElements, backElements) : Geometry();
1417 }
1418
1419 void TextureManager::EmitLoadComplete(TextureUploadObserver* observer, TextureManager::TextureInfo& textureInfo, const bool success)
1420 {
1421   if(textureInfo.storageType == TextureManager::StorageType::RETURN_PIXEL_BUFFER)
1422   {
1423     observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, textureInfo.pixelBuffer, textureInfo.url.GetUrl(), textureInfo.preMultiplied));
1424   }
1425   else
1426   {
1427     TextureSet textureSet = GetTextureSet(textureInfo);
1428     if(textureInfo.isAnimatedImageFormat)
1429     {
1430       observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::ANIMATED_IMAGE_TEXTURE, textureInfo.textureId, textureSet, textureInfo.frameCount, textureInfo.frameInterval));
1431     }
1432     else
1433     {
1434       observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureSet, textureInfo.preMultiplied));
1435     }
1436   }
1437 }
1438
1439 TextureSet TextureManager::GetTextureSet(const TextureManager::TextureId textureId)
1440 {
1441   TextureSet                textureSet;
1442   TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId);
1443   if(loadState == TextureManager::LoadState::UPLOADED)
1444   {
1445     TextureCacheIndex textureCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId);
1446     if(textureCacheIndex != INVALID_CACHE_INDEX)
1447     {
1448       TextureInfo& textureInfo(mTextureCacheManager[textureCacheIndex]);
1449       textureSet = GetTextureSet(textureInfo);
1450     }
1451   }
1452   else
1453   {
1454     DALI_LOG_ERROR("GetTextureSet is failed. texture is not uploaded \n");
1455   }
1456   return textureSet;
1457 }
1458
1459 TextureSet TextureManager::GetTextureSet(const TextureManager::TextureInfo& textureInfo)
1460 {
1461   TextureSet textureSet;
1462
1463   // Always create new TextureSet here, so we don't share same TextureSets for multiple visuals.
1464   textureSet = TextureSet::New();
1465
1466   if(!textureInfo.textures.empty())
1467   {
1468     if(textureInfo.textures.size() > 1) // For YUV case
1469     {
1470       uint32_t index = 0u;
1471       for(auto&& texture : textureInfo.textures)
1472       {
1473         textureSet.SetTexture(index++, texture);
1474       }
1475     }
1476     else
1477     {
1478       textureSet.SetTexture(TEXTURE_INDEX, textureInfo.textures[0]);
1479       TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureInfo.maskTextureId);
1480       if(maskCacheIndex != INVALID_CACHE_INDEX)
1481       {
1482         TextureInfo& maskTextureInfo(mTextureCacheManager[maskCacheIndex]);
1483         if(maskTextureInfo.storageType == TextureManager::StorageType::UPLOAD_TO_TEXTURE || maskTextureInfo.storageType == TextureManager::StorageType::KEEP_TEXTURE)
1484         {
1485           if(!maskTextureInfo.textures.empty())
1486           {
1487             textureSet.SetTexture(MASK_TEXTURE_INDEX, maskTextureInfo.textures[0]);
1488           }
1489         }
1490       }
1491     }
1492   }
1493   return textureSet;
1494 }
1495
1496 void TextureManager::RemoveTextureObserver(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer)
1497 {
1498   if(observer)
1499   {
1500     const auto iterEnd = textureInfo.observerList.End();
1501     const auto iter    = std::find(textureInfo.observerList.Begin(), iterEnd, observer);
1502     if(iter != iterEnd)
1503     {
1504       // Disconnect and remove the observer.
1505       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Disconnect DestructionSignal to observer:%p\n", observer);
1506       observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed);
1507       textureInfo.observerList.Erase(iter);
1508     }
1509     else
1510     {
1511       // Given textureId might exist at load queue.
1512       // Remove observer from the LoadQueue
1513       for(auto&& element : mLoadQueue)
1514       {
1515         if(element.mTextureId == textureInfo.textureId && element.mObserver == observer)
1516         {
1517           DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "Remove observer from observer queue (textureId:%d, observer:%p)\n", element.mTextureId, element.mObserver);
1518           DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Disconnect DestructionSignal to observer:%p\n", observer);
1519           observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed);
1520           element.mObserver = nullptr;
1521           break;
1522         }
1523       }
1524     }
1525   }
1526 }
1527
1528 } // namespace Internal
1529
1530 } // namespace Toolkit
1531
1532 } // namespace Dali