2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/texture-manager/texture-cache-manager.h>
22 #include <dali/devel-api/common/hash.h>
23 #include <dali/integration-api/debug.h>
24 #include <string_view>
25 #include <unordered_map>
37 const std::string_view& GetEncodedImageBufferExtensions(Dali::EncodedImageBuffer::ImageType imageType)
39 static constexpr std::string_view emptyString = "";
40 static const std::unordered_map<Dali::EncodedImageBuffer::ImageType, const std::string_view> gEncodedImageBufferExtensionMap =
42 {Dali::EncodedImageBuffer::ImageType::REGULAR_IMAGE, emptyString},
43 {Dali::EncodedImageBuffer::ImageType::VECTOR_IMAGE, ".svg"},
44 {Dali::EncodedImageBuffer::ImageType::ANIMATED_VECTOR_IMAGE, ".json"},
47 const auto iter = gEncodedImageBufferExtensionMap.find(imageType);
49 if(DALI_LIKELY(iter != gEncodedImageBufferExtensionMap.end()))
58 extern Debug::Filter* gTextureManagerLogFilter; ///< Define at texture-manager-impl.cpp
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" : \
76 // Due to the compile issue, this specialized template code must be defined top of this code.
78 void TextureCacheManager::RemoveTextureInfoByIndex<TextureCacheManager::EncodedImageBufferInfoContainerType>(TextureCacheManager::EncodedImageBufferInfoContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex)
80 // Swap last data of cacheContainer.
81 if(static_cast<std::size_t>(removeContainerIndex.GetIndex() + 1) < cacheContainer.size())
83 // First, change the cache index infomations inside of converter
84 mTextureIdConverter[cacheContainer.back().bufferId] = static_cast<uint32_t>(removeContainerIndex);
86 // After change converter, swap the value between current data and last data.
87 std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back());
90 // Now we can assume that latest data should be removed. pop_back.
91 cacheContainer.pop_back();
94 template<class ContainerType>
95 void TextureCacheManager::RemoveTextureInfoByIndex(ContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex)
97 // Swap last data of cacheContainer.
98 if(static_cast<std::size_t>(removeContainerIndex.GetIndex() + 1) < cacheContainer.size())
100 // First, change the cache index infomations inside of converter
101 mTextureIdConverter[cacheContainer.back().textureId] = static_cast<uint32_t>(removeContainerIndex);
103 // After change converter, swap the value between current data and last data.
104 std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back());
107 // Now we can assume that latest data should be removed. pop_back.
108 cacheContainer.pop_back();
111 TextureCacheManager::TextureCacheManager()
115 TextureCacheManager::~TextureCacheManager()
119 VisualUrl TextureCacheManager::GetVisualUrl(const TextureCacheManager::TextureId textureId)
121 VisualUrl visualUrl("");
122 TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<uint32_t>(textureId)]);
124 switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
126 case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
128 DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture index=%d, textureId=%d\n", cacheIndex.GetIndex(), textureId);
130 TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
131 visualUrl = cachedTextureInfo.url;
134 case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
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));
140 case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER:
142 DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached buffer index=%d, bufferId=%d\n", cacheIndex.GetIndex(), textureId);
144 EncodedImageBufferInfo& cachedEncodedImageBufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
145 const auto& encodedImageBuffer = cachedEncodedImageBufferInfo.encodedImageBuffer;
146 visualUrl = VisualUrl::CreateBufferUrl(std::to_string(textureId), GetEncodedImageBufferExtensions(encodedImageBuffer.GetImageType()));
158 TextureCacheManager::LoadState TextureCacheManager::GetTextureState(const TextureCacheManager::TextureId textureId)
160 LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED;
161 TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<uint32_t>(textureId)]);
163 switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
165 case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
167 TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
168 loadState = cachedTextureInfo.loadState;
171 case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
173 loadState = LoadState::UPLOADED;
185 TextureCacheManager::LoadState TextureCacheManager::GetTextureStateInternal(const TextureCacheManager::TextureId textureId)
187 LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED;
188 TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
189 if(cacheIndex != INVALID_CACHE_INDEX)
191 TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
192 loadState = cachedTextureInfo.loadState;
198 Texture TextureCacheManager::GetTexture(const TextureCacheManager::TextureId textureId, const uint32_t textureIndex)
200 Texture texture; // empty handle
201 TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
203 switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
205 case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
207 TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
208 if(textureIndex < static_cast<uint32_t>(cachedTextureInfo.textures.size()))
210 texture = cachedTextureInfo.textures[textureIndex];
223 TextureCacheManager::ExternalTextureInfo& TextureCacheManager::GetExternalTextureInfo(const TextureCacheManager::TextureId textureId)
225 TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(textureId);
226 DALI_ASSERT_ALWAYS(cacheIndex != INVALID_CACHE_INDEX);
228 return mExternalTextures[cacheIndex.GetIndex()];
231 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId bufferId)
233 EncodedImageBuffer encodedImageBuffer; // empty handle
234 TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(bufferId);
235 if(cacheIndex != INVALID_CACHE_INDEX)
237 encodedImageBuffer = mEncodedImageBuffers[cacheIndex.GetIndex()].encodedImageBuffer;
239 return encodedImageBuffer;
242 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const VisualUrl& url)
244 EncodedImageBuffer encodedImageBuffer; // empty handle
245 if(url.IsValid() && VisualUrl::BUFFER == url.GetProtocolType())
247 std::string location = url.GetLocationWithoutExtension();
248 if(location.size() > 0u)
250 TextureId bufferId = std::stoi(location);
251 return GetEncodedImageBuffer(bufferId);
254 return encodedImageBuffer;
257 std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet, const bool preMultiplied)
259 TextureId textureId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE, mExternalTextures.size()));
261 TextureCacheManager::ExternalTextureInfo textureInfo(textureId, textureSet, preMultiplied);
262 mExternalTextures.emplace_back(textureInfo);
264 DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalTexture() : New texture registered. textureId:%d, preMultiplied:%d\n", textureInfo.textureId, preMultiplied);
266 return VisualUrl::CreateTextureUrl(std::to_string(textureInfo.textureId));
269 std::string TextureCacheManager::AddEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
272 TextureHash bufferHash = static_cast<TextureHash>(encodedImageBuffer.GetHash());
273 TextureCacheIndex bufferCacheIndex = FindCachedEncodedImageBuffer(bufferHash, encodedImageBuffer);
274 if(bufferCacheIndex != INVALID_CACHE_INDEX)
276 EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[bufferCacheIndex.GetIndex()]);
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));
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()));
285 TextureId bufferId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER, mEncodedImageBuffers.size()));
287 TextureCacheManager::EncodedImageBufferInfo info(bufferId, bufferHash, encodedImageBuffer);
288 mEncodedImageBuffers.emplace_back(info);
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);
296 DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : New buffer regested. bufferId:%d\n", info.bufferId);
298 return VisualUrl::CreateBufferUrl(std::to_string(info.bufferId), GetEncodedImageBufferExtensions(encodedImageBuffer.GetImageType()));
301 TextureSet TextureCacheManager::RemoveExternalTexture(const VisualUrl& url)
303 TextureSet textureSet;
304 bool removeTextureInfo = false;
305 TextureCacheIndex removeTextureIndex = INVALID_CACHE_INDEX;
308 if(VisualUrl::TEXTURE == url.GetProtocolType())
310 // get the location from the Url
311 std::string location = url.GetLocation();
312 if(location.size() > 0u)
314 TextureId textureId = std::stoi(location);
315 removeTextureIndex = GetCacheIndexFromExternalTextureId(textureId);
316 if(removeTextureIndex != INVALID_CACHE_INDEX)
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)
323 removeTextureInfo = true;
324 // id life is finished. Remove it at converter
325 mTextureIdConverter.Remove(textureId);
332 // Post removal process to avoid mExternalTextures reference problems.
333 if(removeTextureInfo)
335 // Swap last data of mExternalTextures, and pop_back.
336 RemoveTextureInfoByIndex(mExternalTextures, removeTextureIndex);
341 EncodedImageBuffer TextureCacheManager::RemoveEncodedImageBuffer(const VisualUrl& url)
343 EncodedImageBuffer encodedImageBuffer;
344 bool removeBufferInfo = false;
345 TextureCacheIndex removeBufferIndex = INVALID_CACHE_INDEX;
348 if(VisualUrl::BUFFER == url.GetProtocolType())
350 // get the location from the Url
351 std::string location = url.GetLocationWithoutExtension();
352 if(location.size() > 0u)
354 TextureId bufferId = std::stoi(location);
355 removeBufferIndex = GetCacheIndexFromEncodedImageBufferId(bufferId);
357 if(removeBufferIndex != INVALID_CACHE_INDEX)
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));
362 encodedImageBuffer = bufferInfo.encodedImageBuffer;
363 if(--(bufferInfo.referenceCount) <= 0)
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);
376 // Post removal process to avoid mEncodedImageBuffers reference problems.
379 // Step 3. swap last data of mEncodedImageBuffers, and pop_back.
380 RemoveTextureInfoByIndex(mEncodedImageBuffers, removeBufferIndex);
382 return encodedImageBuffer;
385 void TextureCacheManager::UseExternalResource(const VisualUrl& url)
387 if(VisualUrl::TEXTURE == url.GetProtocolType())
389 std::string location = url.GetLocation();
390 if(location.size() > 0u)
392 TextureId id = std::stoi(location);
393 TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(id);
394 if(cacheIndex != INVALID_CACHE_INDEX)
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));
399 textureInfo.referenceCount++;
404 else if(VisualUrl::BUFFER == url.GetProtocolType())
406 std::string location = url.GetLocationWithoutExtension();
407 if(location.size() > 0u)
409 TextureId id = std::stoi(location);
410 TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id);
411 if(cacheIndex != INVALID_CACHE_INDEX)
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));
416 bufferInfo.referenceCount++;
423 TextureCacheManager::TextureId TextureCacheManager::GenerateTextureId(const TextureCacheIndex& textureCacheIndex)
425 return mTextureIdConverter.Add(static_cast<uint32_t>(textureCacheIndex.indexValue));
428 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromId(const TextureCacheManager::TextureId textureId)
430 if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
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))
435 return INVALID_CACHE_INDEX;
438 DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mTextureInfoContainer.size());
443 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromExternalTextureId(const TextureCacheManager::TextureId textureId)
445 if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
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))
450 return INVALID_CACHE_INDEX;
453 DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mExternalTextures.size());
458 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromEncodedImageBufferId(const TextureCacheManager::TextureId bufferId)
460 if(bufferId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
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))
465 return INVALID_CACHE_INDEX;
468 DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mEncodedImageBuffers.size());
473 TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedEncodedImageBuffer(const TextureCacheManager::TextureHash hash, const EncodedImageBuffer& encodedImageBuffer)
475 // Iterate through our hashes to find a match.
476 const auto& hashIterator = mTextureHashContainer.find(hash);
477 if(hashIterator != mTextureHashContainer.cend())
479 for(const auto& id : hashIterator->second)
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)
485 EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
487 if(bufferInfo.encodedImageBuffer == encodedImageBuffer)
489 // The found encoded image buffer.
496 // Default to an invalid ID, in case we do not find a match.
497 return INVALID_CACHE_INDEX;
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)
509 std::vector<std::uint8_t> hashTarget;
510 const uint16_t width = size.GetWidth();
511 const uint16_t height = size.GetWidth();
513 // If either the width or height has been specified, include the resizing options in the hash
514 if(width != 0 || height != 0)
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]);
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;
526 // Bit-pack the FittingMode, SamplingMode.
527 // FittingMode=2bits, SamplingMode=3bits
528 *hashTargetPtr = (fittingMode << 3u) | (samplingMode);
531 if(maskTextureId != INVALID_TEXTURE_ID)
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]));
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)
542 *hashTargetPtr++ = saltedMaskTextureId & 0xff;
543 saltedMaskTextureId >>= 8u;
545 *hashTargetPtr++ = (cropToMask ? 'C' : 'M');
548 // Append the frameIndex. We don't do additional job when frameIndex = 0u due to the non-animated image case.
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]));
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)
560 *hashTargetPtr++ = saltedFrameIndex & 0xff;
561 saltedFrameIndex >>= 8u;
565 return url.GetUrlHash() ^ Dali::CalculateHash(hashTarget);
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)
581 // Iterate through our hashes to find a match.
582 const auto& hashIterator = mTextureHashContainer.find(hash);
583 if(hashIterator != mTextureHashContainer.cend())
585 for(const auto& textureId : hashIterator->second)
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)
591 TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
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)))
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))
608 // The found Texture is a match.
616 // Default to an invalid ID, in case we do not find a match.
617 return INVALID_CACHE_INDEX;
620 TextureCacheManager::TextureCacheIndex TextureCacheManager::AppendCache(const TextureCacheManager::TextureInfo& textureInfo)
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)
626 UseExternalResource(textureInfo.url);
629 TextureHash hash = textureInfo.hash;
630 TextureId id = textureInfo.textureId;
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);
638 // Insert TextureInfo back of mTextureInfoContainer.
639 TextureCacheIndex cacheIndex = TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, mTextureInfoContainer.size());
640 mTextureInfoContainer.emplace_back(textureInfo);
642 // Add converter id --> cacheIndex
643 // NOTE : We should assume that id already generated by GenerateTextureId function.
644 mTextureIdConverter[id] = cacheIndex;
649 void TextureCacheManager::RemoveCache(TextureCacheManager::TextureInfo& textureInfo)
651 TextureCacheIndex textureInfoIndex = GetCacheIndexFromId(textureInfo.textureId);
652 bool removeTextureInfo = false;
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);
656 // Decrement the reference count and check if this is the last user of this Texture.
657 if(--textureInfo.referenceCount <= 0)
659 // This is the last remove for this Texture.
660 textureInfo.referenceCount = 0;
662 // If loaded, we can remove the TextureInfo
663 if(textureInfo.loadState == LoadState::UPLOADED)
665 removeTextureInfo = true;
667 else if(textureInfo.loadState == LoadState::LOADING)
669 // We mark the textureInfo for removal.
670 // Once the load has completed, this method will be called again.
671 textureInfo.loadState = LoadState::CANCELLED;
673 else if(textureInfo.loadState == LoadState::MASK_APPLYING)
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;
681 // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
682 removeTextureInfo = true;
685 // If the state allows us to remove the TextureInfo data, we do so.
686 if(removeTextureInfo)
688 // If url location is BUFFER, decrease reference count of EncodedImageBuffer.
689 if(textureInfo.url.IsBufferResource())
691 RemoveEncodedImageBuffer(textureInfo.url.GetUrl());
694 // Permanently remove the textureInfo struct.
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);
703 // Post removal process to avoid mTextureInfoContainer reference problems.
704 if(removeTextureInfo)
706 // Step 3. swap last data of TextureInfoContainer, and pop_back.
707 RemoveTextureInfoByIndex(mTextureInfoContainer, textureInfoIndex);
711 void TextureCacheManager::RemoveHashId(const TextureCacheManager::TextureHash textureHash, const TextureCacheManager::TextureId textureId)
713 auto hashIterator = mTextureHashContainer.find(textureHash);
714 if(hashIterator != mTextureHashContainer.end())
716 auto hashIdList = hashIterator->second;
717 const auto& hashIdIterator = std::find(hashIdList.cbegin(), hashIdList.cend(), textureId);
718 if(hashIdIterator != hashIdList.cend())
720 hashIdList.erase(hashIdIterator);
721 if(hashIdList.size() == 0)
723 // If id list in current hash is empty, remove it self in the container.
724 mTextureHashContainer.erase(hashIterator);
730 } // namespace Internal
732 } // namespace Toolkit