Trigger svg loading early
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / npatch-loader.cpp
1 /*
2  * Copyright (c) 2022 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/visuals/npatch-loader.h>
20
21 // INTERNAL HEADERS
22 #include <dali-toolkit/internal/visuals/rendering-addon.h>
23
24 // EXTERNAL HEADERS
25 #include <dali/devel-api/common/hash.h>
26 #include <dali/integration-api/debug.h>
27
28 namespace Dali
29 {
30 namespace Toolkit
31 {
32 namespace Internal
33 {
34 namespace
35 {
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
38
39 } // Anonymous namespace
40
41 NPatchLoader::NPatchLoader()
42 : mCurrentNPatchDataId(0)
43 {
44 }
45
46 NPatchLoader::~NPatchLoader()
47 {
48 }
49
50 NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
51 {
52   return mCurrentNPatchDataId++;
53 }
54
55 std::size_t NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading)
56 {
57   NPatchData* data = GetNPatchData(url, border, preMultiplyOnLoad);
58
59   DALI_ASSERT_ALWAYS(data && "NPatchData creation failed!");
60
61   if(data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
62   {
63     if(!synchronousLoading)
64     {
65       // NotifyObserver already done, so
66       // data will not iterate observer list.
67       // We need to call LoadComplete directly.
68       data->NotifyObserver(textureObserver, true);
69     }
70   }
71   else // if NOT_STARTED or LOADING or LOAD_FAILED, try to reload.
72   {
73     if(!synchronousLoading)
74     {
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)
78       {
79         return data->GetId();
80       }
81     }
82
83     data->SetLoadingState(NPatchData::LoadingState::LOADING);
84
85     auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
86                                                   : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
87
88     Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data, true, preMultiplyOnLoading);
89
90     if(pixelBuffer)
91     {
92       preMultiplyOnLoad = (preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) ? true : false;
93       data->SetLoadedNPatchData(pixelBuffer, preMultiplyOnLoad);
94     }
95     else if(synchronousLoading)
96     {
97       data->SetLoadingState(NPatchData::LoadingState::LOAD_FAILED);
98     }
99   }
100   return data->GetId();
101 }
102
103 int32_t NPatchLoader::GetCacheIndexFromId(const NPatchData::NPatchDataId id)
104 {
105   const unsigned int size = mCache.size();
106
107   for(unsigned int i = 0; i < size; ++i)
108   {
109     if(mCache[i].mData->GetId() == id)
110     {
111       return i;
112     }
113   }
114
115   return INVALID_CACHE_INDEX;
116 }
117
118 bool NPatchLoader::GetNPatchData(const NPatchData::NPatchDataId id, const NPatchData*& data)
119 {
120   int32_t cacheIndex = GetCacheIndexFromId(id);
121   if(cacheIndex != INVALID_CACHE_INDEX)
122   {
123     data = mCache[cacheIndex].mData;
124     return true;
125   }
126   data = nullptr;
127   return false;
128 }
129
130 void NPatchLoader::Remove(std::size_t id, TextureUploadObserver* textureObserver)
131 {
132   int32_t cacheIndex = GetCacheIndexFromId(id);
133   if(cacheIndex == INVALID_CACHE_INDEX)
134   {
135     return;
136   }
137
138   NPatchInfo& info(mCache[cacheIndex]);
139
140   info.mData->RemoveObserver(textureObserver);
141
142   if(--info.mReferenceCount <= 0)
143   {
144     mCache.erase(mCache.begin() + cacheIndex);
145   }
146 }
147
148 NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad)
149 {
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();
153
154   NPatchInfo* infoPtr = nullptr;
155
156   for(; index < count; ++index)
157   {
158     if(mCache[index].mData->GetHash() == hash)
159     {
160       // hash match, check url as well in case of hash collision
161       if(mCache[index].mData->GetUrl().GetUrl() == url.GetUrl())
162       {
163         // Use cached data. Need to fast-out return.
164         if(mCache[index].mData->GetBorder() == border)
165         {
166           mCache[index].mReferenceCount++;
167           return mCache[index].mData;
168         }
169         else
170         {
171           if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
172           {
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)
175             {
176               infoPtr = &mCache[index];
177             }
178           }
179           // Still loading pixel buffer. We cannot reuse cached texture yet. Skip checking
180           else if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOADING)
181           {
182             continue;
183           }
184           // if LOAD_FAILED, reuse this cached NPatchData, and try to load again.
185           else
186           {
187             if(infoPtr == nullptr)
188             {
189               infoPtr = &mCache[index];
190             }
191           }
192         }
193       }
194     }
195   }
196
197   // If this is new image loading, make new cache data
198   if(infoPtr == nullptr)
199   {
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);
206
207     mCache.emplace_back(std::move(info));
208     infoPtr = &mCache.back();
209   }
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)
212   {
213     NPatchInfo info(new NPatchData());
214
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());
220
221     info.mData->SetTextures(infoPtr->mData->GetTextures());
222
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)));
225
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)));
228
229     info.mData->SetStretchPixelsX(stretchRangesX);
230     info.mData->SetStretchPixelsY(stretchRangesY);
231     info.mData->SetBorder(border);
232
233     info.mData->SetPreMultiplyOnLoad(infoPtr->mData->IsPreMultiplied());
234
235     info.mData->SetLoadingState(NPatchData::LoadingState::LOAD_COMPLETE);
236
237     mCache.emplace_back(std::move(info));
238     infoPtr = &mCache.back();
239   }
240   // Else, LOAD_FAILED. just increase reference so we can reuse it.
241   else
242   {
243     infoPtr->mReferenceCount++;
244   }
245
246   DALI_ASSERT_ALWAYS(infoPtr && "NPatchInfo creation failed!");
247
248   return infoPtr->mData;
249 }
250
251 } // namespace Internal
252
253 } // namespace Toolkit
254
255 } // namespace Dali