2 * Copyright (c) 2022 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/debug.h>
36 constexpr auto INVALID_CACHE_INDEX = int32_t{-1}; ///< Invalid Cache index
37 constexpr auto UNINITIALIZED_ID = int32_t{0}; ///< uninitialised id, use to initialize ids
39 } // Anonymous namespace
41 NPatchLoader::NPatchLoader()
42 : mCurrentNPatchDataId(0)
46 NPatchLoader::~NPatchLoader()
50 NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
52 return mCurrentNPatchDataId++;
55 std::size_t NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading)
57 NPatchData* data = GetNPatchData(url, border, preMultiplyOnLoad);
59 DALI_ASSERT_ALWAYS(data && "NPatchData creation failed!");
61 if(data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
63 if(!synchronousLoading)
65 // NotifyObserver already done, so
66 // data will not iterate observer list.
67 // We need to call LoadComplete directly.
68 data->NotifyObserver(textureObserver, true);
71 else // if NOT_STARTED or LOADING or LOAD_FAILED, try to reload.
73 if(!synchronousLoading)
75 data->AddObserver(textureObserver);
76 // If still LOADING and async, don't need to request reload. Fast return.
77 if(data->GetLoadingState() == NPatchData::LoadingState::LOADING)
83 data->SetLoadingState(NPatchData::LoadingState::LOADING);
85 auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
86 : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
88 Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data, true, preMultiplyOnLoading);
92 preMultiplyOnLoad = (preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) ? true : false;
93 data->SetLoadedNPatchData(pixelBuffer, preMultiplyOnLoad);
95 else if(synchronousLoading)
97 data->SetLoadingState(NPatchData::LoadingState::LOAD_FAILED);
100 return data->GetId();
103 int32_t NPatchLoader::GetCacheIndexFromId(const NPatchData::NPatchDataId id)
105 const unsigned int size = mCache.size();
107 for(unsigned int i = 0; i < size; ++i)
109 if(mCache[i].mData->GetId() == id)
115 return INVALID_CACHE_INDEX;
118 bool NPatchLoader::GetNPatchData(const NPatchData::NPatchDataId id, const NPatchData*& data)
120 int32_t cacheIndex = GetCacheIndexFromId(id);
121 if(cacheIndex != INVALID_CACHE_INDEX)
123 data = mCache[cacheIndex].mData.get();
130 void NPatchLoader::Remove(std::size_t id, TextureUploadObserver* textureObserver)
132 int32_t cacheIndex = GetCacheIndexFromId(id);
133 if(cacheIndex == INVALID_CACHE_INDEX)
138 NPatchInfo& info(mCache[cacheIndex]);
140 info.mData->RemoveObserver(textureObserver);
142 if(--info.mReferenceCount <= 0)
144 mCache.erase(mCache.begin() + cacheIndex);
148 NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad)
150 std::size_t hash = CalculateHash(url.GetUrl());
151 std::vector<NPatchInfo>::size_type index = UNINITIALIZED_ID;
152 const std::vector<NPatchInfo>::size_type count = mCache.size();
154 NPatchInfo* infoPtr = nullptr;
156 for(; index < count; ++index)
158 if(mCache[index].mData->GetHash() == hash)
160 // hash match, check url as well in case of hash collision
161 if(mCache[index].mData->GetUrl().GetUrl() == url.GetUrl())
163 // Use cached data. Need to fast-out return.
164 if(mCache[index].mData->GetBorder() == border)
166 mCache[index].mReferenceCount++;
167 return mCache[index].mData.get();
171 if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
173 // If we only found LOAD_FAILED case, replace current data. We can reuse texture
174 if(infoPtr == nullptr || infoPtr->mData->GetLoadingState() != NPatchData::LoadingState::LOAD_COMPLETE)
176 infoPtr = &mCache[index];
179 // Still loading pixel buffer. We cannot reuse cached texture yet. Skip checking
180 else if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOADING)
184 // if LOAD_FAILED, reuse this cached NPatchData, and try to load again.
187 if(infoPtr == nullptr)
189 infoPtr = &mCache[index];
197 // If this is new image loading, make new cache data
198 if(infoPtr == nullptr)
200 NPatchInfo info(new NPatchData());
201 info.mData->SetId(GenerateUniqueNPatchDataId());
202 info.mData->SetHash(hash);
203 info.mData->SetUrl(url);
204 info.mData->SetBorder(border);
205 info.mData->SetPreMultiplyOnLoad(preMultiplyOnLoad);
207 mCache.emplace_back(std::move(info));
208 infoPtr = &mCache.back();
210 // Else if LOAD_COMPLETE, Same url but border is different - use the existing texture
211 else if(infoPtr->mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
213 NPatchInfo info(new NPatchData());
215 info.mData->SetId(GenerateUniqueNPatchDataId());
216 info.mData->SetHash(hash);
217 info.mData->SetUrl(url);
218 info.mData->SetCroppedWidth(infoPtr->mData->GetCroppedWidth());
219 info.mData->SetCroppedHeight(infoPtr->mData->GetCroppedHeight());
221 info.mData->SetTextures(infoPtr->mData->GetTextures());
223 NPatchUtility::StretchRanges stretchRangesX;
224 stretchRangesX.PushBack(Uint16Pair(border.left, ((info.mData->GetCroppedWidth() >= static_cast<unsigned int>(border.right)) ? info.mData->GetCroppedHeight() - border.right : 0)));
226 NPatchUtility::StretchRanges stretchRangesY;
227 stretchRangesY.PushBack(Uint16Pair(border.top, ((info.mData->GetCroppedWidth() >= static_cast<unsigned int>(border.bottom)) ? info.mData->GetCroppedHeight() - border.bottom : 0)));
229 info.mData->SetStretchPixelsX(stretchRangesX);
230 info.mData->SetStretchPixelsY(stretchRangesY);
231 info.mData->SetBorder(border);
233 info.mData->SetPreMultiplyOnLoad(infoPtr->mData->IsPreMultiplied());
235 info.mData->SetLoadingState(NPatchData::LoadingState::LOAD_COMPLETE);
237 mCache.emplace_back(std::move(info));
238 infoPtr = &mCache.back();
240 // Else, LOAD_FAILED. just increase reference so we can reuse it.
243 infoPtr->mReferenceCount++;
246 DALI_ASSERT_ALWAYS(infoPtr && "NPatchInfo creation failed!");
248 return infoPtr->mData.get();
251 } // namespace Internal
253 } // namespace Toolkit