2550001067b70ffeb87d02d2465192ad59182369
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / npatch-loader.cpp
1  /*
2  * Copyright (c) 2020 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
31 namespace Toolkit
32 {
33
34 namespace Internal
35 {
36
37 namespace NPatchBuffer
38 {
39
40 void SetLoadedNPatchData( NPatchLoader::Data* data, Devel::PixelBuffer& pixelBuffer )
41 {
42   if( data->border == Rect< int >( 0, 0, 0, 0 ) )
43   {
44     NPatchUtility::ParseBorders( pixelBuffer, data->stretchPixelsX, data->stretchPixelsY );
45
46     // Crop the image
47     pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 );
48   }
49   else
50   {
51     data->stretchPixelsX.PushBack( Uint16Pair( data->border.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( data->border.right )) ? pixelBuffer.GetWidth() - data->border.right : 0 ) ) );
52     data->stretchPixelsY.PushBack( Uint16Pair( data->border.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( data->border.bottom )) ? pixelBuffer.GetHeight() - data->border.bottom : 0 ) ) );
53   }
54
55   data->croppedWidth = pixelBuffer.GetWidth();
56   data->croppedHeight = pixelBuffer.GetHeight();
57
58   // Create opacity map
59   data->renderingMap = RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().BuildNPatch(pixelBuffer, data ) : nullptr;
60
61   PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer
62
63   Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
64   texture.Upload( pixels );
65
66   data->textureSet = TextureSet::New();
67   data->textureSet.SetTexture( 0u, texture );
68
69   data->loadCompleted = true;
70 }
71
72 } // namespace NPatchBuffer
73
74 NPatchLoader::Data::~Data()
75 {
76   // If there is an opacity map, it has to be destroyed using addon call
77   if( renderingMap )
78   {
79     RenderingAddOn::Get().DestroyNPatch( renderingMap );
80   }
81 }
82
83 NPatchLoader::NPatchLoader()
84 {
85 }
86
87 NPatchLoader::~NPatchLoader()
88 {
89 }
90
91 std::size_t NPatchLoader::Load( TextureManager& textureManager, TextureUploadObserver* textureObserver, const std::string& url, const Rect< int >& border, bool& preMultiplyOnLoad, bool synchronousLoading )
92 {
93   std::size_t hash = CalculateHash( url );
94   OwnerContainer< Data* >::SizeType index = UNINITIALIZED_ID;
95   const OwnerContainer< Data* >::SizeType count = mCache.Count();
96   int cachedIndex = -1;
97   Data* data;
98
99   for( ; index < count; ++index )
100   {
101     if( mCache[ index ]->hash == hash )
102     {
103       // hash match, check url as well in case of hash collision
104       if( mCache[ index ]->url == url )
105       {
106         // Use cached data
107         if( mCache[ index ]->border == border )
108         {
109           if( mCache[ index ]->loadCompleted )
110           {
111             return index + 1u; // valid indices are from 1 onwards
112           }
113           mCache[ index ]->observerList.PushBack( textureObserver );
114           data = mCache[ index ];
115           return index + 1u; // valid indices are from 1 onwards
116         }
117         else
118         {
119           if( mCache[ index ]->loadCompleted )
120           {
121             // Same url but border is different - use the existing texture
122             Data* data = new Data();
123             data->hash = hash;
124             data->url = url;
125             data->croppedWidth = mCache[ index ]->croppedWidth;
126             data->croppedHeight = mCache[ index ]->croppedHeight;
127
128             data->textureSet = mCache[ index ]->textureSet;
129
130             NPatchUtility::StretchRanges stretchRangesX;
131             stretchRangesX.PushBack( Uint16Pair( border.left, ( (data->croppedWidth >= static_cast< unsigned int >( border.right )) ? data->croppedWidth - border.right : 0 ) ) );
132
133             NPatchUtility::StretchRanges stretchRangesY;
134             stretchRangesY.PushBack( Uint16Pair( border.top, ( (data->croppedHeight >= static_cast< unsigned int >( border.bottom )) ? data->croppedHeight - border.bottom : 0 ) ) );
135
136             data->stretchPixelsX = stretchRangesX;
137             data->stretchPixelsY = stretchRangesY;
138             data->border = border;
139
140             data->loadCompleted = mCache[ index ]->loadCompleted;
141
142             mCache.PushBack( data );
143
144             return mCache.Count(); // valid ids start from 1u
145           }
146         }
147       }
148     }
149   }
150
151   if( cachedIndex == -1 )
152   {
153     data = new Data();
154     data->loadCompleted = false;
155     data->hash = hash;
156     data->url = url;
157     data->border = border;
158
159     mCache.PushBack( data );
160
161     cachedIndex = mCache.Count();
162   }
163
164   auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
165                                                 : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
166   Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer( url, Dali::ImageDimensions(), FittingMode::DEFAULT,
167                                                                    SamplingMode::BOX_THEN_LINEAR, synchronousLoading,
168                                                                    textureObserver, true, preMultiplyOnLoading );
169
170   if( pixelBuffer )
171   {
172     NPatchBuffer::SetLoadedNPatchData( data, pixelBuffer );
173     preMultiplyOnLoad = ( preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD ) ? true : false;
174   }
175
176   return cachedIndex;
177 }
178
179 void NPatchLoader::SetNPatchData( bool loadSuccess, std::size_t id, Devel::PixelBuffer& pixelBuffer, const Internal::VisualUrl& url, bool preMultiplied )
180 {
181   Data* data;
182   data = mCache[ id - 1u ];
183
184   // To prevent recursion.
185   // data->loadCompleted will be set true in the NPatchBuffer::SetLoadedNPatchData when the first observer called this method.
186   if( data->loadCompleted )
187   {
188     return;
189   }
190
191   NPatchBuffer::SetLoadedNPatchData( data, pixelBuffer );
192
193   while( data->observerList.Count() )
194   {
195     TextureUploadObserver* observer = data->observerList[0];
196     observer->LoadComplete( loadSuccess, Devel::PixelBuffer(), url, preMultiplied );
197     data->observerList.Erase( data->observerList.begin() );
198   }
199 }
200
201 bool NPatchLoader::GetNPatchData( std::size_t id, const Data*& data )
202 {
203   if( ( id > UNINITIALIZED_ID )&&( id <= mCache.Count() ) )
204   {
205     data = mCache[ id - 1u ]; // id's start from 1u
206     return true;
207   }
208   data = NULL;
209   return false;
210 }
211
212 } // namespace Internal
213
214 } // namespace Toolkit
215
216 } // namespace Dali