[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / npatch-data.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/visuals/npatch-data.h>
20
21 // INTERNAL HEADERS
22 #include <dali-toolkit/internal/visuals/rendering-addon.h>
23
24 // EXTERNAL HEADERS
25 #include <dali/integration-api/debug.h>
26
27 namespace Dali
28 {
29 namespace Toolkit
30 {
31 namespace Internal
32 {
33 NPatchData::NPatchData()
34 : mId(INVALID_NPATCH_DATA_ID),
35   mUrl(),
36   mTextureSet(),
37   mHash(0),
38   mCroppedWidth(0),
39   mCroppedHeight(0),
40   mBorder(0, 0, 0, 0),
41   mLoadingState(LoadingState::NOT_STARTED),
42   mRenderingMap{nullptr},
43   mPreMultiplyOnLoad(false),
44   mObserverNotifying(false)
45 {
46 }
47
48 NPatchData::~NPatchData()
49 {
50   // If there is an opacity map, it has to be destroyed using addon call
51   if(mRenderingMap)
52   {
53     RenderingAddOn::Get().DestroyNPatch(mRenderingMap);
54   }
55   mObserverList.Clear();
56   mQueuedObservers.Clear();
57 }
58
59 void NPatchData::SetId(const NPatchDataId id)
60 {
61   mId = id;
62 }
63
64 NPatchData::NPatchDataId NPatchData::GetId() const
65 {
66   return mId;
67 }
68
69 void NPatchData::AddObserver(TextureUploadObserver* textureObserver)
70 {
71   if(textureObserver)
72   {
73     if(mObserverNotifying)
74     {
75       // Do not add it into observer list during observer notifying.
76       mQueuedObservers.PushBack(textureObserver);
77     }
78     else
79     {
80       mObserverList.PushBack(textureObserver);
81     }
82     textureObserver->DestructionSignal().Connect(this, &NPatchData::ObserverDestroyed);
83   }
84 }
85
86 void NPatchData::RemoveObserver(TextureUploadObserver* textureObserver)
87 {
88   if(textureObserver)
89   {
90     for(uint32_t index = 0; index < mObserverList.Count(); ++index)
91     {
92       if(textureObserver == mObserverList[index])
93       {
94         textureObserver->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed);
95         mObserverList.Erase(mObserverList.begin() + index);
96         break;
97       }
98     }
99   }
100 }
101
102 uint32_t NPatchData::GetObserverCount() const
103 {
104   return mObserverList.Count();
105 }
106
107 void NPatchData::SetUrl(const VisualUrl& url)
108 {
109   mUrl = url;
110 }
111
112 VisualUrl NPatchData::GetUrl() const
113 {
114   return mUrl;
115 }
116
117 void NPatchData::SetTextures(const TextureSet textureSet)
118 {
119   mTextureSet = textureSet;
120 }
121
122 TextureSet NPatchData::GetTextures() const
123 {
124   return mTextureSet;
125 }
126
127 void NPatchData::SetStretchPixelsX(const NPatchUtility::StretchRanges stretchPixelsX)
128 {
129   mStretchPixelsX = stretchPixelsX;
130 }
131
132 void NPatchData::SetStretchPixelsY(const NPatchUtility::StretchRanges stretchPixelsY)
133 {
134   mStretchPixelsY = stretchPixelsY;
135 }
136
137 NPatchUtility::StretchRanges NPatchData::GetStretchPixelsX() const
138 {
139   return mStretchPixelsX;
140 }
141
142 NPatchUtility::StretchRanges NPatchData::GetStretchPixelsY() const
143 {
144   return mStretchPixelsY;
145 }
146
147 void NPatchData::SetHash(std::size_t hash)
148 {
149   mHash = hash;
150 }
151
152 std::size_t NPatchData::GetHash() const
153 {
154   return mHash;
155 }
156
157 void NPatchData::SetCroppedWidth(uint32_t croppedWidth)
158 {
159   mCroppedWidth = croppedWidth;
160 }
161
162 void NPatchData::SetCroppedHeight(uint32_t croppedHeight)
163 {
164   mCroppedHeight = croppedHeight;
165 }
166
167 uint32_t NPatchData::GetCroppedWidth() const
168 {
169   return mCroppedWidth;
170 }
171
172 uint32_t NPatchData::GetCroppedHeight() const
173 {
174   return mCroppedHeight;
175 }
176
177 void NPatchData::SetBorder(const Rect<int> border)
178 {
179   mBorder = border;
180 }
181
182 Rect<int> NPatchData::GetBorder() const
183 {
184   return mBorder;
185 }
186
187 void NPatchData::SetPreMultiplyOnLoad(bool preMultiplyOnLoad)
188 {
189   mPreMultiplyOnLoad = preMultiplyOnLoad;
190 }
191
192 bool NPatchData::IsPreMultiplied() const
193 {
194   return mPreMultiplyOnLoad;
195 }
196
197 void NPatchData::SetLoadingState(const LoadingState loadingState)
198 {
199   mLoadingState = loadingState;
200 }
201
202 NPatchData::LoadingState NPatchData::GetLoadingState() const
203 {
204   return mLoadingState;
205 }
206
207 void* NPatchData::GetRenderingMap() const
208 {
209   return mRenderingMap;
210 }
211
212 void NPatchData::SetLoadedNPatchData(Devel::PixelBuffer& pixelBuffer, bool preMultiplied)
213 {
214   if(mBorder == Rect<int>(0, 0, 0, 0))
215   {
216     NPatchUtility::ParseBorders(pixelBuffer, mStretchPixelsX, mStretchPixelsY);
217
218     // Crop the image
219     pixelBuffer.Crop(1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2);
220   }
221   else
222   {
223     mStretchPixelsX.PushBack(Uint16Pair(mBorder.left, ((pixelBuffer.GetWidth() >= static_cast<unsigned int>(mBorder.right)) ? pixelBuffer.GetWidth() - mBorder.right : 0)));
224     mStretchPixelsY.PushBack(Uint16Pair(mBorder.top, ((pixelBuffer.GetHeight() >= static_cast<unsigned int>(mBorder.bottom)) ? pixelBuffer.GetHeight() - mBorder.bottom : 0)));
225   }
226
227   mCroppedWidth  = pixelBuffer.GetWidth();
228   mCroppedHeight = pixelBuffer.GetHeight();
229
230   // Create opacity map
231   mRenderingMap = RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().BuildNPatch(pixelBuffer, this) : nullptr;
232
233   PixelData pixels = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
234
235   Texture texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
236   texture.Upload(pixels);
237
238   mTextureSet = TextureSet::New();
239   mTextureSet.SetTexture(0u, texture);
240
241   mPreMultiplyOnLoad = preMultiplied;
242
243   mLoadingState = LoadingState::LOAD_COMPLETE;
244 }
245
246 void NPatchData::NotifyObserver(TextureUploadObserver* observer, const bool& loadSuccess)
247 {
248   observer->LoadComplete(
249     loadSuccess,
250     TextureUploadObserver::TextureInformation(
251       TextureUploadObserver::ReturnType::TEXTURE,
252       static_cast<TextureManager::TextureId>(mId), ///< Note : until end of NPatchLoader::Load, npatch-visual don't know the id of data.
253       mTextureSet,
254       mUrl.GetUrl(),
255       mPreMultiplyOnLoad));
256 }
257
258 void NPatchData::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
259 {
260   NPatchDataPtr self = this; // Keep reference until this API finished
261
262   if(loadSuccess)
263   {
264     if(mLoadingState != LoadingState::LOAD_COMPLETE)
265     {
266       // If mLoadingState is LOAD_FAILED, just re-set (It can be happened when sync loading is failed, but async loading is succeeded).
267       SetLoadedNPatchData(textureInformation.pixelBuffer, textureInformation.preMultiplied);
268     }
269   }
270   else
271   {
272     if(mLoadingState == LoadingState::LOADING)
273     {
274       mLoadingState = LoadingState::LOAD_FAILED;
275     }
276     // If mLoadingState is already LOAD_COMPLETE, we can use uploaded texture (It can be happened when sync loading is succeeded, but async loading is failed).
277     else if(mLoadingState == LoadingState::LOAD_COMPLETE)
278     {
279       loadSuccess = true;
280     }
281   }
282
283   mObserverNotifying = true;
284
285   // Reverse observer list that we can pop_back the observer.
286   std::reverse(mObserverList.Begin(), mObserverList.End());
287
288   while(mObserverList.Count() > 0u)
289   {
290     TextureUploadObserver* observer = *(mObserverList.End() - 1u);
291     mObserverList.Erase(mObserverList.End() - 1u);
292
293     observer->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed);
294
295     NotifyObserver(observer, loadSuccess);
296   }
297
298   mObserverNotifying = false;
299
300   // Swap observer list what we queued during notify observer.
301   // If mQueuedObserver is not empty, it mean mLoadingState was LOAD_FAILED, and we try to re-load for this data.
302   // (If mLoadingState was LOAD_COMPLETE, NotifyObserver will be called directly. @todo : Should we fix this logic, matched with texture manager?)
303   // So LoadComplete will be called.
304   mObserverList.Swap(mQueuedObservers);
305 }
306
307 void NPatchData::ObserverDestroyed(TextureUploadObserver* observer)
308 {
309   for(auto iter = mObserverList.Begin(); iter != mObserverList.End();)
310   {
311     if(observer == (*iter))
312     {
313       iter = mObserverList.Erase(iter);
314     }
315     else
316     {
317       ++iter;
318     }
319   }
320   if(mObserverNotifying)
321   {
322     for(auto iter = mQueuedObservers.Begin(); iter != mQueuedObservers.End();)
323     {
324       if(observer == (*iter))
325       {
326         iter = mQueuedObservers.Erase(iter);
327       }
328       else
329       {
330         ++iter;
331       }
332     }
333   }
334 }
335
336 } // namespace Internal
337
338 } // namespace Toolkit
339
340 } // namespace Dali