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