[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / texture-manager / texture-cache-manager.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-cache-manager.h>
20
21 // EXTERNAL HEADERS
22 #include <dali/devel-api/common/hash.h>
23 #include <dali/integration-api/debug.h>
24 #include <string_view>
25 #include <unordered_map>
26
27 // INTERNAL HEADERS
28
29 namespace Dali
30 {
31 namespace Toolkit
32 {
33 namespace Internal
34 {
35 namespace
36 {
37 const std::string_view& GetEncodedImageBufferExtensions(Dali::EncodedImageBuffer::ImageType imageType)
38 {
39   static constexpr std::string_view                                                            emptyString = "";
40   static const std::unordered_map<Dali::EncodedImageBuffer::ImageType, const std::string_view> gEncodedImageBufferExtensionMap =
41     {
42       {Dali::EncodedImageBuffer::ImageType::REGULAR_IMAGE, emptyString},
43       {Dali::EncodedImageBuffer::ImageType::VECTOR_IMAGE, ".svg"},
44       {Dali::EncodedImageBuffer::ImageType::ANIMATED_VECTOR_IMAGE, ".json"},
45     };
46
47   const auto iter = gEncodedImageBufferExtensionMap.find(imageType);
48
49   if(DALI_LIKELY(iter != gEncodedImageBufferExtensionMap.end()))
50   {
51     return iter->second;
52   }
53
54   return emptyString;
55 }
56 } // namespace
57 #ifdef DEBUG_ENABLED
58 extern Debug::Filter* gTextureManagerLogFilter; ///< Define at texture-manager-impl.cpp
59
60 // clang-format off
61 #define GET_LOAD_STATE_STRING(loadState) \
62   loadState == TextureManagerType::LoadState::NOT_STARTED      ? "NOT_STARTED"      : \
63   loadState == TextureManagerType::LoadState::LOADING          ? "LOADING"          : \
64   loadState == TextureManagerType::LoadState::LOAD_FINISHED    ? "LOAD_FINISHED"    : \
65   loadState == TextureManagerType::LoadState::WAITING_FOR_MASK ? "WAITING_FOR_MASK" : \
66   loadState == TextureManagerType::LoadState::MASK_APPLYING    ? "MASK_APPLYING"    : \
67   loadState == TextureManagerType::LoadState::MASK_APPLIED     ? "MASK_APPLIED"     : \
68   loadState == TextureManagerType::LoadState::UPLOADED         ? "UPLOADED"         : \
69   loadState == TextureManagerType::LoadState::CANCELLED        ? "CANCELLED"        : \
70   loadState == TextureManagerType::LoadState::MASK_CANCELLED   ? "MASK_CANCELLED"   : \
71   loadState == TextureManagerType::LoadState::LOAD_FAILED      ? "LOAD_FAILED"      : \
72                                                                  "Unknown"
73 // clang-format on
74 #endif
75
76 // Due to the compile issue, this specialized template code must be defined top of this code.
77 template<>
78 void TextureCacheManager::RemoveTextureInfoByIndex<TextureCacheManager::EncodedImageBufferInfoContainerType>(TextureCacheManager::EncodedImageBufferInfoContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex)
79 {
80   // Swap last data of cacheContainer.
81   if(static_cast<std::size_t>(removeContainerIndex.GetIndex() + 1) < cacheContainer.size())
82   {
83     // First, change the cache index infomations inside of converter
84     mTextureIdConverter[cacheContainer.back().bufferId] = static_cast<uint32_t>(removeContainerIndex);
85
86     // After change converter, swap the value between current data and last data.
87     std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back());
88   }
89
90   // Now we can assume that latest data should be removed. pop_back.
91   cacheContainer.pop_back();
92 }
93
94 template<class ContainerType>
95 void TextureCacheManager::RemoveTextureInfoByIndex(ContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex)
96 {
97   // Swap last data of cacheContainer.
98   if(static_cast<std::size_t>(removeContainerIndex.GetIndex() + 1) < cacheContainer.size())
99   {
100     // First, change the cache index infomations inside of converter
101     mTextureIdConverter[cacheContainer.back().textureId] = static_cast<uint32_t>(removeContainerIndex);
102
103     // After change converter, swap the value between current data and last data.
104     std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back());
105   }
106
107   // Now we can assume that latest data should be removed. pop_back.
108   cacheContainer.pop_back();
109 }
110
111 TextureCacheManager::TextureCacheManager()
112 {
113 }
114
115 TextureCacheManager::~TextureCacheManager()
116 {
117 }
118
119 VisualUrl TextureCacheManager::GetVisualUrl(const TextureCacheManager::TextureId textureId)
120 {
121   VisualUrl         visualUrl("");
122   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<uint32_t>(textureId)]);
123
124   switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
125   {
126     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
127     {
128       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture index=%d, textureId=%d\n", cacheIndex.GetIndex(), textureId);
129
130       TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
131       visualUrl = cachedTextureInfo.url;
132       break;
133     }
134     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
135     {
136       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached external texture index=%d, textureId=%d\n", cacheIndex.GetIndex(), textureId);
137       visualUrl = VisualUrl::CreateTextureUrl(std::to_string(textureId));
138       break;
139     }
140     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER:
141     {
142       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached buffer index=%d, bufferId=%d\n", cacheIndex.GetIndex(), textureId);
143
144       EncodedImageBufferInfo& cachedEncodedImageBufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
145       const auto&             encodedImageBuffer = cachedEncodedImageBufferInfo.encodedImageBuffer;
146       visualUrl                                  = VisualUrl::CreateBufferUrl(std::to_string(textureId), GetEncodedImageBufferExtensions(encodedImageBuffer.GetImageType()));
147       break;
148     }
149     default:
150     {
151       break;
152     }
153   }
154
155   return visualUrl;
156 }
157
158 TextureCacheManager::LoadState TextureCacheManager::GetTextureState(const TextureCacheManager::TextureId textureId)
159 {
160   LoadState         loadState  = TextureCacheManager::LoadState::NOT_STARTED;
161   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<uint32_t>(textureId)]);
162
163   switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
164   {
165     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
166     {
167       TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
168       loadState = cachedTextureInfo.loadState;
169       break;
170     }
171     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
172     {
173       loadState = LoadState::UPLOADED;
174       break;
175     }
176     default:
177     {
178       break;
179     }
180   }
181
182   return loadState;
183 }
184
185 TextureCacheManager::LoadState TextureCacheManager::GetTextureStateInternal(const TextureCacheManager::TextureId textureId)
186 {
187   LoadState         loadState  = TextureCacheManager::LoadState::NOT_STARTED;
188   TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
189   if(cacheIndex != INVALID_CACHE_INDEX)
190   {
191     TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
192     loadState = cachedTextureInfo.loadState;
193   }
194
195   return loadState;
196 }
197
198 Texture TextureCacheManager::GetTexture(const TextureCacheManager::TextureId textureId, const uint32_t textureIndex)
199 {
200   Texture           texture; // empty handle
201   TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
202
203   switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
204   {
205     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
206     {
207       TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
208       if(textureIndex < static_cast<uint32_t>(cachedTextureInfo.textures.size()))
209       {
210         texture = cachedTextureInfo.textures[textureIndex];
211       }
212       break;
213     }
214     default:
215     {
216       break;
217     }
218   }
219
220   return texture;
221 }
222
223 TextureCacheManager::ExternalTextureInfo& TextureCacheManager::GetExternalTextureInfo(const TextureCacheManager::TextureId textureId)
224 {
225   TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(textureId);
226   DALI_ASSERT_ALWAYS(cacheIndex != INVALID_CACHE_INDEX);
227
228   return mExternalTextures[cacheIndex.GetIndex()];
229 }
230
231 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId bufferId)
232 {
233   EncodedImageBuffer encodedImageBuffer; // empty handle
234   TextureCacheIndex  cacheIndex = GetCacheIndexFromEncodedImageBufferId(bufferId);
235   if(cacheIndex != INVALID_CACHE_INDEX)
236   {
237     encodedImageBuffer = mEncodedImageBuffers[cacheIndex.GetIndex()].encodedImageBuffer;
238   }
239   return encodedImageBuffer;
240 }
241
242 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const VisualUrl& url)
243 {
244   EncodedImageBuffer encodedImageBuffer; // empty handle
245   if(url.IsValid() && VisualUrl::BUFFER == url.GetProtocolType())
246   {
247     std::string location = url.GetLocationWithoutExtension();
248     if(location.size() > 0u)
249     {
250       TextureId bufferId = std::stoi(location);
251       return GetEncodedImageBuffer(bufferId);
252     }
253   }
254   return encodedImageBuffer;
255 }
256
257 std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet, const bool preMultiplied)
258 {
259   TextureId textureId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE, mExternalTextures.size()));
260
261   TextureCacheManager::ExternalTextureInfo textureInfo(textureId, textureSet, preMultiplied);
262   mExternalTextures.emplace_back(textureInfo);
263
264   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalTexture() : New texture registered. textureId:%d, preMultiplied:%d\n", textureInfo.textureId, preMultiplied);
265
266   return VisualUrl::CreateTextureUrl(std::to_string(textureInfo.textureId));
267 }
268
269 std::string TextureCacheManager::AddEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
270 {
271   // Duplication check
272   TextureHash       bufferHash       = static_cast<TextureHash>(encodedImageBuffer.GetHash());
273   TextureCacheIndex bufferCacheIndex = FindCachedEncodedImageBuffer(bufferHash, encodedImageBuffer);
274   if(bufferCacheIndex != INVALID_CACHE_INDEX)
275   {
276     EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[bufferCacheIndex.GetIndex()]);
277
278     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : Increase reference. bufferId:%d, cache index:%d, reference:%d\n", bufferInfo.bufferId, bufferCacheIndex.GetIndex(), static_cast<int>(bufferInfo.referenceCount));
279
280     // If same buffer added, increase reference count and return.
281     bufferInfo.referenceCount++;
282     return VisualUrl::CreateBufferUrl(std::to_string(bufferInfo.bufferId), GetEncodedImageBufferExtensions(encodedImageBuffer.GetImageType()));
283   }
284
285   TextureId bufferId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER, mEncodedImageBuffers.size()));
286
287   TextureCacheManager::EncodedImageBufferInfo info(bufferId, bufferHash, encodedImageBuffer);
288   mEncodedImageBuffers.emplace_back(info);
289
290   // Insert TextureHashContainer
291   // Find exist list -or- Create new list.
292   std::vector<TextureId>& idList = mTextureHashContainer[bufferHash];
293   // We already assume that list doesn't contain id. just emplace back
294   idList.emplace_back(bufferId);
295
296   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : New buffer regested. bufferId:%d\n", info.bufferId);
297
298   return VisualUrl::CreateBufferUrl(std::to_string(info.bufferId), GetEncodedImageBufferExtensions(encodedImageBuffer.GetImageType()));
299 }
300
301 TextureSet TextureCacheManager::RemoveExternalTexture(const VisualUrl& url)
302 {
303   TextureSet        textureSet;
304   bool              removeTextureInfo  = false;
305   TextureCacheIndex removeTextureIndex = INVALID_CACHE_INDEX;
306   if(url.IsValid())
307   {
308     if(VisualUrl::TEXTURE == url.GetProtocolType())
309     {
310       // get the location from the Url
311       std::string location = url.GetLocation();
312       if(location.size() > 0u)
313       {
314         TextureId textureId = std::stoi(location);
315         removeTextureIndex  = GetCacheIndexFromExternalTextureId(textureId);
316         if(removeTextureIndex != INVALID_CACHE_INDEX)
317         {
318           ExternalTextureInfo& textureInfo(mExternalTextures[removeTextureIndex.GetIndex()]);
319           DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::RemoveExternalTexture(url:%s) textureId:%d reference:%d\n", url.GetUrl().c_str(), textureId, static_cast<int>(textureInfo.referenceCount));
320           textureSet = textureInfo.textureSet;
321           if(--(textureInfo.referenceCount) <= 0)
322           {
323             removeTextureInfo = true;
324             // id life is finished. Remove it at converter
325             mTextureIdConverter.Remove(textureId);
326           }
327         }
328       }
329     }
330   }
331
332   // Post removal process to avoid mExternalTextures reference problems.
333   if(removeTextureInfo)
334   {
335     // Swap last data of mExternalTextures, and pop_back.
336     RemoveTextureInfoByIndex(mExternalTextures, removeTextureIndex);
337   }
338   return textureSet;
339 }
340
341 EncodedImageBuffer TextureCacheManager::RemoveEncodedImageBuffer(const VisualUrl& url)
342 {
343   EncodedImageBuffer encodedImageBuffer;
344   bool               removeBufferInfo  = false;
345   TextureCacheIndex  removeBufferIndex = INVALID_CACHE_INDEX;
346   if(url.IsValid())
347   {
348     if(VisualUrl::BUFFER == url.GetProtocolType())
349     {
350       // get the location from the Url
351       std::string location = url.GetLocationWithoutExtension();
352       if(location.size() > 0u)
353       {
354         TextureId bufferId = std::stoi(location);
355         removeBufferIndex  = GetCacheIndexFromEncodedImageBufferId(bufferId);
356
357         if(removeBufferIndex != INVALID_CACHE_INDEX)
358         {
359           EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[removeBufferIndex.GetIndex()]);
360           DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::RemoveEncodedImageBuffer(url:%s) bufferId:%d reference:%d\n", url.GetUrl().c_str(), bufferId, static_cast<int>(bufferInfo.referenceCount));
361
362           encodedImageBuffer = bufferInfo.encodedImageBuffer;
363           if(--(bufferInfo.referenceCount) <= 0)
364           {
365             removeBufferInfo = true;
366             // Step 1. remove current textureId information in mTextureHashContainer.
367             RemoveHashId(bufferInfo.bufferHash, bufferId);
368             // Step 2. id life is finished. Remove it at converter
369             mTextureIdConverter.Remove(bufferId);
370           }
371         }
372       }
373     }
374   }
375
376   // Post removal process to avoid mEncodedImageBuffers reference problems.
377   if(removeBufferInfo)
378   {
379     // Step 3. swap last data of mEncodedImageBuffers, and pop_back.
380     RemoveTextureInfoByIndex(mEncodedImageBuffers, removeBufferIndex);
381   }
382   return encodedImageBuffer;
383 }
384
385 void TextureCacheManager::UseExternalResource(const VisualUrl& url)
386 {
387   if(VisualUrl::TEXTURE == url.GetProtocolType())
388   {
389     std::string location = url.GetLocation();
390     if(location.size() > 0u)
391     {
392       TextureId         id         = std::stoi(location);
393       TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(id);
394       if(cacheIndex != INVALID_CACHE_INDEX)
395       {
396         ExternalTextureInfo& textureInfo(mExternalTextures[cacheIndex.GetIndex()]);
397         DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::UseExternalResource(url:%s) type:TEXTURE, location:%s, reference:%d\n", url.GetUrl().c_str(), url.GetLocation().c_str(), static_cast<int>(textureInfo.referenceCount));
398
399         textureInfo.referenceCount++;
400         return;
401       }
402     }
403   }
404   else if(VisualUrl::BUFFER == url.GetProtocolType())
405   {
406     std::string location = url.GetLocationWithoutExtension();
407     if(location.size() > 0u)
408     {
409       TextureId         id         = std::stoi(location);
410       TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id);
411       if(cacheIndex != INVALID_CACHE_INDEX)
412       {
413         EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
414         DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::UseExternalResource(url:%s) type:BUFFER, location:%s, reference:%d\n", url.GetUrl().c_str(), url.GetLocation().c_str(), static_cast<int>(bufferInfo.referenceCount));
415
416         bufferInfo.referenceCount++;
417         return;
418       }
419     }
420   }
421 }
422
423 TextureCacheManager::TextureId TextureCacheManager::GenerateTextureId(const TextureCacheIndex& textureCacheIndex)
424 {
425   return mTextureIdConverter.Add(static_cast<uint32_t>(textureCacheIndex.indexValue));
426 }
427
428 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromId(const TextureCacheManager::TextureId textureId)
429 {
430   if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
431
432   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<uint32_t>(textureId)]);
433   if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL))
434   {
435     return INVALID_CACHE_INDEX;
436   }
437
438   DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mTextureInfoContainer.size());
439
440   return cacheIndex;
441 }
442
443 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromExternalTextureId(const TextureCacheManager::TextureId textureId)
444 {
445   if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
446
447   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<uint32_t>(textureId)]);
448   if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE))
449   {
450     return INVALID_CACHE_INDEX;
451   }
452
453   DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mExternalTextures.size());
454
455   return cacheIndex;
456 }
457
458 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromEncodedImageBufferId(const TextureCacheManager::TextureId bufferId)
459 {
460   if(bufferId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
461
462   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<uint32_t>(bufferId)]);
463   if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER))
464   {
465     return INVALID_CACHE_INDEX;
466   }
467
468   DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mEncodedImageBuffers.size());
469
470   return cacheIndex;
471 }
472
473 TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedEncodedImageBuffer(const TextureCacheManager::TextureHash hash, const EncodedImageBuffer& encodedImageBuffer)
474 {
475   // Iterate through our hashes to find a match.
476   const auto& hashIterator = mTextureHashContainer.find(hash);
477   if(hashIterator != mTextureHashContainer.cend())
478   {
479     for(const auto& id : hashIterator->second)
480     {
481       // We have a match, now we check all the original parameters in case of a hash collision.
482       TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id);
483       if(cacheIndex != INVALID_CACHE_INDEX)
484       {
485         EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
486
487         if(bufferInfo.encodedImageBuffer == encodedImageBuffer)
488         {
489           // The found encoded image buffer.
490           return cacheIndex;
491         }
492       }
493     }
494   }
495
496   // Default to an invalid ID, in case we do not find a match.
497   return INVALID_CACHE_INDEX;
498 }
499
500 TextureCacheManager::TextureHash TextureCacheManager::GenerateHash(
501   const VisualUrl&                     url,
502   const Dali::ImageDimensions&         size,
503   const Dali::FittingMode::Type        fittingMode,
504   const Dali::SamplingMode::Type       samplingMode,
505   const TextureCacheManager::TextureId maskTextureId,
506   const bool                           cropToMask,
507   const uint32_t                       frameIndex)
508 {
509   std::vector<std::uint8_t> hashTarget;
510   const uint16_t            width  = size.GetWidth();
511   const uint16_t            height = size.GetWidth();
512
513   // If either the width or height has been specified, include the resizing options in the hash
514   if(width != 0 || height != 0)
515   {
516     // We are appending 5 bytes to the URL to form the hash input.
517     hashTarget.resize(5u);
518     std::uint8_t* hashTargetPtr = &(hashTarget[0u]);
519
520     // Pack the width and height (4 bytes total).
521     *hashTargetPtr++ = size.GetWidth() & 0xff;
522     *hashTargetPtr++ = (size.GetWidth() >> 8u) & 0xff;
523     *hashTargetPtr++ = size.GetHeight() & 0xff;
524     *hashTargetPtr++ = (size.GetHeight() >> 8u) & 0xff;
525
526     // Bit-pack the FittingMode, SamplingMode.
527     // FittingMode=2bits, SamplingMode=3bits
528     *hashTargetPtr = (fittingMode << 3u) | (samplingMode);
529   }
530
531   if(maskTextureId != INVALID_TEXTURE_ID)
532   {
533     auto textureIdIndex = hashTarget.size();
534     hashTarget.resize(hashTarget.size() + sizeof(TextureId) + 1u);
535     std::uint8_t* hashTargetPtr = reinterpret_cast<std::uint8_t*>(&(hashTarget[textureIdIndex]));
536
537     // Append the texture id to the end of the URL byte by byte:
538     // (to avoid SIGBUS / alignment issues)
539     TextureId saltedMaskTextureId = maskTextureId;
540     for(size_t byteIter = 0; byteIter < sizeof(TextureId); ++byteIter)
541     {
542       *hashTargetPtr++ = saltedMaskTextureId & 0xff;
543       saltedMaskTextureId >>= 8u;
544     }
545     *hashTargetPtr++ = (cropToMask ? 'C' : 'M');
546   }
547
548   // Append the frameIndex. We don't do additional job when frameIndex = 0u due to the non-animated image case.
549   if(frameIndex > 0u)
550   {
551     auto textureIdIndex = hashTarget.size();
552     hashTarget.resize(hashTarget.size() + sizeof(uint32_t));
553     std::uint8_t* hashTargetPtr = reinterpret_cast<std::uint8_t*>(&(hashTarget[textureIdIndex]));
554
555     // Append the frame index to the end of the URL byte by byte:
556     // (to avoid SIGBUS / alignment issues)
557     uint32_t saltedFrameIndex = frameIndex;
558     for(size_t byteIter = 0; byteIter < sizeof(uint32_t); ++byteIter)
559     {
560       *hashTargetPtr++ = saltedFrameIndex & 0xff;
561       saltedFrameIndex >>= 8u;
562     }
563   }
564
565   return url.GetUrlHash() ^ Dali::CalculateHash(hashTarget);
566 }
567
568 TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture(
569   const TextureCacheManager::TextureHash    hash,
570   const VisualUrl&                          url,
571   const Dali::ImageDimensions&              size,
572   const Dali::FittingMode::Type             fittingMode,
573   const Dali::SamplingMode::Type            samplingMode,
574   const TextureCacheManager::StorageType    storageType,
575   const TextureCacheManager::TextureId      maskTextureId,
576   const bool                                cropToMask,
577   const TextureCacheManager::MultiplyOnLoad preMultiplyOnLoad,
578   const bool                                isAnimatedImage,
579   const uint32_t                            frameIndex)
580 {
581   // Iterate through our hashes to find a match.
582   const auto& hashIterator = mTextureHashContainer.find(hash);
583   if(hashIterator != mTextureHashContainer.cend())
584   {
585     for(const auto& textureId : hashIterator->second)
586     {
587       // We have a match, now we check all the original parameters in case of a hash collision.
588       TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
589       if(cacheIndex != INVALID_CACHE_INDEX)
590       {
591         TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
592
593         if((url.GetUrl() == textureInfo.url.GetUrl()) &&
594            (maskTextureId == textureInfo.maskTextureId) &&
595            (cropToMask == textureInfo.cropToMask) &&
596            (size == textureInfo.desiredSize) &&
597            (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
598            (storageType == textureInfo.storageType) &&
599            (frameIndex == textureInfo.frameIndex) &&
600            ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
601             (fittingMode == textureInfo.fittingMode &&
602              samplingMode == textureInfo.samplingMode)))
603         {
604           // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
605           // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
606           if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied))
607           {
608             // The found Texture is a match.
609             return cacheIndex;
610           }
611         }
612       }
613     }
614   }
615
616   // Default to an invalid ID, in case we do not find a match.
617   return INVALID_CACHE_INDEX;
618 }
619
620 TextureCacheManager::TextureCacheIndex TextureCacheManager::AppendCache(const TextureCacheManager::TextureInfo& textureInfo)
621 {
622   // If we use EncodedImageBuffer, increase reference during it contains mTextureInfoContainer.
623   // This reference will be decreased when we call RemoveCache
624   if(textureInfo.url.GetProtocolType() == VisualUrl::BUFFER)
625   {
626     UseExternalResource(textureInfo.url);
627   }
628
629   TextureHash hash = textureInfo.hash;
630   TextureId   id   = textureInfo.textureId;
631
632   // Insert TextureHash container first
633   // Find exist list -or- Create new list.
634   std::vector<TextureId>& idList = mTextureHashContainer[hash];
635   // We already assume that list doesn't contain id. just emplace back
636   idList.emplace_back(id);
637
638   // Insert TextureInfo back of mTextureInfoContainer.
639   TextureCacheIndex cacheIndex = TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, mTextureInfoContainer.size());
640   mTextureInfoContainer.emplace_back(textureInfo);
641
642   // Add converter id --> cacheIndex
643   // NOTE : We should assume that id already generated by GenerateTextureId function.
644   mTextureIdConverter[id] = cacheIndex;
645
646   return cacheIndex;
647 }
648
649 void TextureCacheManager::RemoveCache(TextureCacheManager::TextureInfo& textureInfo)
650 {
651   TextureCacheIndex textureInfoIndex  = GetCacheIndexFromId(textureInfo.textureId);
652   bool              removeTextureInfo = false;
653
654   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::Remove(textureId:%d) url:%s\n  cacheIdx:%d loadState:%s reference count = %d\n", textureInfo.textureId, textureInfo.url.GetUrl().c_str(), textureInfoIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState), textureInfo.referenceCount);
655
656   // Decrement the reference count and check if this is the last user of this Texture.
657   if(--textureInfo.referenceCount <= 0)
658   {
659     // This is the last remove for this Texture.
660     textureInfo.referenceCount = 0;
661
662     // If loaded, we can remove the TextureInfo
663     if(textureInfo.loadState == LoadState::UPLOADED)
664     {
665       removeTextureInfo = true;
666     }
667     else if(textureInfo.loadState == LoadState::LOADING)
668     {
669       // We mark the textureInfo for removal.
670       // Once the load has completed, this method will be called again.
671       textureInfo.loadState = LoadState::CANCELLED;
672     }
673     else if(textureInfo.loadState == LoadState::MASK_APPLYING)
674     {
675       // We mark the textureInfo for removal.
676       // Once the load has completed, this method will be called again.
677       textureInfo.loadState = LoadState::MASK_CANCELLED;
678     }
679     else
680     {
681       // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
682       removeTextureInfo = true;
683     }
684
685     // If the state allows us to remove the TextureInfo data, we do so.
686     if(removeTextureInfo)
687     {
688       // If url location is BUFFER, decrease reference count of EncodedImageBuffer.
689       if(textureInfo.url.IsBufferResource())
690       {
691         RemoveEncodedImageBuffer(textureInfo.url.GetUrl());
692       }
693
694       // Permanently remove the textureInfo struct.
695
696       // Step 1. remove current textureId information in mTextureHashContainer.
697       RemoveHashId(textureInfo.hash, textureInfo.textureId);
698       // Step 2. make textureId is not using anymore. After this job, we can reuse textureId.
699       mTextureIdConverter.Remove(textureInfo.textureId);
700     }
701   }
702
703   // Post removal process to avoid mTextureInfoContainer reference problems.
704   if(removeTextureInfo)
705   {
706     // Step 3. swap last data of TextureInfoContainer, and pop_back.
707     RemoveTextureInfoByIndex(mTextureInfoContainer, textureInfoIndex);
708   }
709 }
710
711 void TextureCacheManager::RemoveHashId(const TextureCacheManager::TextureHash textureHash, const TextureCacheManager::TextureId textureId)
712 {
713   auto hashIterator = mTextureHashContainer.find(textureHash);
714   if(hashIterator != mTextureHashContainer.end())
715   {
716     auto        hashIdList     = hashIterator->second;
717     const auto& hashIdIterator = std::find(hashIdList.cbegin(), hashIdList.cend(), textureId);
718     if(hashIdIterator != hashIdList.cend())
719     {
720       hashIdList.erase(hashIdIterator);
721       if(hashIdList.size() == 0)
722       {
723         // If id list in current hash is empty, remove it self in the container.
724         mTextureHashContainer.erase(hashIterator);
725       }
726     }
727   }
728 }
729
730 } // namespace Internal
731
732 } // namespace Toolkit
733
734 } // namespace Dali