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/visuals/npatch-loader.h>
22 #include <dali-toolkit/internal/visuals/rendering-addon.h>
25 #include <dali/devel-api/common/hash.h>
26 #include <dali/integration-api/adaptor-framework/adaptor.h>
27 #include <dali/integration-api/debug.h>
28 #include <dali/integration-api/trace.h>
38 constexpr auto INVALID_CACHE_INDEX = int32_t{-1}; ///< Invalid Cache index
39 constexpr auto UNINITIALIZED_ID = int32_t{0}; ///< uninitialised id, use to initialize ids
41 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_IMAGE_PERFORMANCE_MARKER, false);
42 } // Anonymous namespace
44 NPatchLoader::NPatchLoader()
45 : mCurrentNPatchDataId(0),
46 mRemoveProcessorRegistered(false)
50 NPatchLoader::~NPatchLoader()
52 if(mRemoveProcessorRegistered && Adaptor::IsAvailable())
54 Adaptor::Get().UnregisterProcessor(*this, true);
55 mRemoveProcessorRegistered = false;
59 NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
61 // Skip invalid id generation.
62 if(DALI_UNLIKELY(mCurrentNPatchDataId == NPatchData::INVALID_NPATCH_DATA_ID))
64 mCurrentNPatchDataId = 0;
66 return mCurrentNPatchDataId++;
69 NPatchData::NPatchDataId NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading)
71 NPatchDataPtr data = GetNPatchData(url, border, preMultiplyOnLoad);
73 DALI_ASSERT_ALWAYS(data.Get() && "NPatchData creation failed!");
75 if(data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
77 if(!synchronousLoading)
79 // NotifyObserver already done, so
80 // data will not iterate observer list.
81 // We need to call LoadComplete directly.
82 data->NotifyObserver(textureObserver, true);
85 else // if NOT_STARTED or LOADING or LOAD_FAILED, try to reload.
87 if(!synchronousLoading)
89 data->AddObserver(textureObserver);
90 // If still LOADING and async, don't need to request reload. Fast return.
91 if(data->GetLoadingState() == NPatchData::LoadingState::LOADING)
97 data->SetLoadingState(NPatchData::LoadingState::LOADING);
99 auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
100 : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
102 Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data.Get(), true, preMultiplyOnLoading);
106 preMultiplyOnLoad = (preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) ? true : false;
107 data->SetLoadedNPatchData(pixelBuffer, preMultiplyOnLoad);
109 else if(synchronousLoading)
111 data->SetLoadingState(NPatchData::LoadingState::LOAD_FAILED);
114 return data->GetId();
117 int32_t NPatchLoader::GetCacheIndexFromId(const NPatchData::NPatchDataId id)
119 const unsigned int size = mCache.size();
121 for(unsigned int i = 0; i < size; ++i)
123 if(mCache[i].mData->GetId() == id)
129 return INVALID_CACHE_INDEX;
132 bool NPatchLoader::GetNPatchData(const NPatchData::NPatchDataId id, NPatchDataPtr& data)
134 int32_t cacheIndex = GetCacheIndexFromId(id);
135 if(cacheIndex != INVALID_CACHE_INDEX)
137 data = mCache[cacheIndex].mData;
144 void NPatchLoader::RequestRemove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver)
146 // Remove observer first
149 int32_t cacheIndex = GetCacheIndexFromId(id);
150 if(cacheIndex != INVALID_CACHE_INDEX)
152 NPatchInfo& info(mCache[cacheIndex]);
154 info.mData->RemoveObserver(textureObserver);
158 mRemoveQueue.push_back({id, nullptr});
160 if(!mRemoveProcessorRegistered && Adaptor::IsAvailable())
162 mRemoveProcessorRegistered = true;
163 Adaptor::Get().RegisterProcessor(*this, true);
167 void NPatchLoader::Remove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver)
169 int32_t cacheIndex = GetCacheIndexFromId(id);
170 if(cacheIndex == INVALID_CACHE_INDEX)
175 NPatchInfo& info(mCache[cacheIndex]);
177 info.mData->RemoveObserver(textureObserver);
179 if(--info.mReferenceCount <= 0)
181 mCache.erase(mCache.begin() + cacheIndex);
185 void NPatchLoader::Process(bool postProcessor)
188 if(gTraceFilter && gTraceFilter->IsTraceEnabled())
190 if(mRemoveQueue.size() > 0u)
192 std::ostringstream oss;
193 oss << "[" << mRemoveQueue.size() << "]";
194 DALI_TRACE_BEGIN_WITH_MESSAGE(gTraceFilter, "DALI_NPATCH_LOADER_PROCESS_REMOVE_QUEUE", oss.str().c_str());
199 for(auto& iter : mRemoveQueue)
201 Remove(iter.first, iter.second);
205 if(gTraceFilter && gTraceFilter->IsTraceEnabled())
207 if(mRemoveQueue.size() > 0u)
209 std::ostringstream oss;
210 oss << "[" << mRemoveQueue.size() << "]";
211 DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_NPATCH_LOADER_PROCESS_REMOVE_QUEUE", oss.str().c_str());
216 mRemoveQueue.clear();
218 if(Adaptor::IsAvailable())
220 Adaptor::Get().UnregisterProcessor(*this, true);
221 mRemoveProcessorRegistered = false;
225 NPatchDataPtr NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad)
227 std::size_t hash = CalculateHash(url.GetUrl());
228 std::vector<NPatchInfo>::size_type index = UNINITIALIZED_ID;
229 const std::vector<NPatchInfo>::size_type count = mCache.size();
231 NPatchInfo* infoPtr = nullptr;
233 for(; index < count; ++index)
235 if(mCache[index].mData->GetHash() == hash)
237 // hash match, check url as well in case of hash collision
238 if(mCache[index].mData->GetUrl().GetUrl() == url.GetUrl())
240 // Use cached data. Need to fast-out return.
241 if(mCache[index].mData->GetBorder() == border)
243 mCache[index].mReferenceCount++;
244 return mCache[index].mData;
248 if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
250 // If we only found LOAD_FAILED case, replace current data. We can reuse texture
251 if(infoPtr == nullptr || infoPtr->mData->GetLoadingState() != NPatchData::LoadingState::LOAD_COMPLETE)
253 infoPtr = &mCache[index];
256 // Still loading pixel buffer. We cannot reuse cached texture yet. Skip checking
257 else if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOADING)
261 // if LOAD_FAILED, reuse this cached NPatchData, and try to load again.
264 if(infoPtr == nullptr)
266 infoPtr = &mCache[index];
274 // If this is new image loading, make new cache data
275 if(infoPtr == nullptr)
277 NPatchInfo info(new NPatchData());
278 info.mData->SetId(GenerateUniqueNPatchDataId());
279 info.mData->SetHash(hash);
280 info.mData->SetUrl(url);
281 info.mData->SetBorder(border);
282 info.mData->SetPreMultiplyOnLoad(preMultiplyOnLoad);
284 mCache.emplace_back(std::move(info));
285 infoPtr = &mCache.back();
287 // Else if LOAD_COMPLETE, Same url but border is different - use the existing texture
288 else if(infoPtr->mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
290 NPatchInfo info(new NPatchData());
292 info.mData->SetId(GenerateUniqueNPatchDataId());
293 info.mData->SetHash(hash);
294 info.mData->SetUrl(url);
295 info.mData->SetCroppedWidth(infoPtr->mData->GetCroppedWidth());
296 info.mData->SetCroppedHeight(infoPtr->mData->GetCroppedHeight());
298 info.mData->SetTextures(infoPtr->mData->GetTextures());
300 NPatchUtility::StretchRanges stretchRangesX;
301 stretchRangesX.PushBack(Uint16Pair(border.left, ((info.mData->GetCroppedWidth() >= static_cast<unsigned int>(border.right)) ? info.mData->GetCroppedHeight() - border.right : 0)));
303 NPatchUtility::StretchRanges stretchRangesY;
304 stretchRangesY.PushBack(Uint16Pair(border.top, ((info.mData->GetCroppedWidth() >= static_cast<unsigned int>(border.bottom)) ? info.mData->GetCroppedHeight() - border.bottom : 0)));
306 info.mData->SetStretchPixelsX(stretchRangesX);
307 info.mData->SetStretchPixelsY(stretchRangesY);
308 info.mData->SetBorder(border);
310 info.mData->SetPreMultiplyOnLoad(infoPtr->mData->IsPreMultiplied());
312 info.mData->SetLoadingState(NPatchData::LoadingState::LOAD_COMPLETE);
314 mCache.emplace_back(std::move(info));
315 infoPtr = &mCache.back();
317 // Else, LOAD_FAILED. just increase reference so we can reuse it.
320 infoPtr->mReferenceCount++;
323 DALI_ASSERT_ALWAYS(infoPtr && "NPatchInfo creation failed!");
325 return infoPtr->mData;
328 } // namespace Internal
330 } // namespace Toolkit