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