Merge "Transfer uploadOnly flag to the core and update manager" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / utility / npatch-utilities.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/devel-api/utility/npatch-utilities.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23
24 namespace Dali
25 {
26 namespace Toolkit
27 {
28 namespace NPatchUtility
29 {
30 namespace
31 {
32 Uint16Pair ParseRange(uint32_t& index, uint32_t width, uint8_t*& pixel, uint32_t pixelStride, int32_t testByte, int32_t testBits, int32_t testValue)
33 {
34   unsigned int start = 0xFFFF;
35   for(; index < width; ++index, pixel += pixelStride)
36   {
37     if((pixel[testByte] & testBits) == testValue)
38     {
39       start = index;
40       ++index;
41       pixel += pixelStride;
42       break;
43     }
44   }
45
46   unsigned int end = width;
47   for(; index < width; ++index, pixel += pixelStride)
48   {
49     if((pixel[testByte] & testBits) != testValue)
50     {
51       end = index;
52       ++index;
53       pixel += pixelStride;
54       break;
55     }
56   }
57
58   return Uint16Pair(start, end);
59 }
60
61 } // unnamed namespace
62
63 void GetRedOffsetAndMask(Dali::Pixel::Format pixelFormat, int32_t& byteOffset, int32_t& bitMask)
64 {
65   switch(pixelFormat)
66   {
67     case Dali::Pixel::A8:
68     case Dali::Pixel::L8:
69     case Dali::Pixel::LA88:
70     {
71       byteOffset = 0;
72       bitMask    = 0;
73       break;
74     }
75     case Dali::Pixel::RGB888:
76     case Dali::Pixel::RGB8888:
77     case Dali::Pixel::RGBA8888:
78     {
79       byteOffset = 0;
80       bitMask    = 0xFF;
81       break;
82     }
83     case Dali::Pixel::BGR8888:
84     case Dali::Pixel::BGRA8888:
85     {
86       byteOffset = 2;
87       bitMask    = 0xff;
88       break;
89     }
90     case Dali::Pixel::RGB565:
91     {
92       byteOffset = 0;
93       bitMask    = 0xf8;
94       break;
95     }
96     case Dali::Pixel::BGR565:
97     {
98       byteOffset = 1;
99       bitMask    = 0x1f;
100       break;
101     }
102     case Dali::Pixel::RGBA4444:
103     {
104       byteOffset = 0;
105       bitMask    = 0xf0;
106       break;
107     }
108     case Dali::Pixel::BGRA4444:
109     {
110       byteOffset = 1;
111       bitMask    = 0xf0;
112       break;
113     }
114     case Dali::Pixel::RGBA5551:
115     {
116       byteOffset = 0;
117       bitMask    = 0xf8;
118       break;
119     }
120     case Dali::Pixel::BGRA5551:
121     {
122       byteOffset = 1;
123       bitMask    = 0x1e;
124       break;
125     }
126     case Dali::Pixel::INVALID:
127     case Dali::Pixel::COMPRESSED_R11_EAC:
128     case Dali::Pixel::COMPRESSED_SIGNED_R11_EAC:
129     case Dali::Pixel::COMPRESSED_RG11_EAC:
130     case Dali::Pixel::COMPRESSED_SIGNED_RG11_EAC:
131     case Dali::Pixel::COMPRESSED_RGB8_ETC2:
132     case Dali::Pixel::COMPRESSED_SRGB8_ETC2:
133     case Dali::Pixel::COMPRESSED_RGB8_ETC1:
134     case Dali::Pixel::COMPRESSED_RGB_PVRTC_4BPPV1:
135     case Dali::Pixel::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
136     case Dali::Pixel::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
137     case Dali::Pixel::COMPRESSED_RGBA8_ETC2_EAC:
138     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
139     case Dali::Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR:
140     case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x4_KHR:
141     case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x5_KHR:
142     case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x5_KHR:
143     case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x6_KHR:
144     case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x5_KHR:
145     case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x6_KHR:
146     case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x8_KHR:
147     case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x5_KHR:
148     case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR:
149     case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x8_KHR:
150     case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x10_KHR:
151     case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x10_KHR:
152     case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x12_KHR:
153     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
154     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
155     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
156     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
157     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
158     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
159     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
160     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
161     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
162     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
163     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
164     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
165     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
166     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
167     {
168       DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple masking-out of per-pixel alpha.\n");
169       byteOffset = 0;
170       bitMask    = 0;
171       break;
172     }
173     case Dali::Pixel::RGB16F:
174     case Dali::Pixel::RGB32F:
175     case Dali::Pixel::DEPTH_UNSIGNED_INT:
176     case Dali::Pixel::DEPTH_FLOAT:
177     case Dali::Pixel::DEPTH_STENCIL:
178     case Dali::Pixel::R11G11B10F:
179     case Dali::Pixel::CHROMINANCE_U:
180     case Dali::Pixel::CHROMINANCE_V:
181     {
182       DALI_LOG_ERROR("Pixel format not compatible.\n");
183       byteOffset = 0;
184       bitMask    = 0;
185       break;
186     }
187   }
188 }
189
190 void ParseBorders(Devel::PixelBuffer& pixelBuffer, StretchRanges& stretchPixelsX, StretchRanges& stretchPixelsY)
191 {
192   stretchPixelsX.Clear();
193   stretchPixelsY.Clear();
194
195   Pixel::Format pixelFormat = pixelBuffer.GetPixelFormat();
196
197   int32_t alphaByte = 0;
198   int32_t alphaBits = 0;
199   Pixel::GetAlphaOffsetAndMask(pixelFormat, alphaByte, alphaBits);
200
201   int32_t testByte  = alphaByte;
202   int32_t testBits  = alphaBits;
203   int32_t testValue = alphaBits; // Opaque == stretch
204   if(!alphaBits)
205   {
206     GetRedOffsetAndMask(pixelFormat, testByte, testBits);
207     testValue = 0; // Black == stretch
208   }
209
210   uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(pixelFormat);
211   uint32_t width         = pixelBuffer.GetWidth();
212   uint32_t height        = pixelBuffer.GetHeight();
213   uint8_t* srcPixels     = pixelBuffer.GetBuffer();
214   uint32_t srcStride     = width * bytesPerPixel;
215
216   // TOP
217   uint8_t* top   = srcPixels + bytesPerPixel;
218   uint32_t index = 0;
219
220   for(; index < width - 2;)
221   {
222     Uint16Pair range = ParseRange(index, width - 2, top, bytesPerPixel, testByte, testBits, testValue);
223     if(range.GetX() != 0xFFFF)
224     {
225       stretchPixelsX.PushBack(range);
226     }
227   }
228
229   // LEFT
230   uint8_t* left = srcPixels + srcStride;
231   index         = 0;
232   for(; index < height - 2;)
233   {
234     Uint16Pair range = ParseRange(index, height - 2, left, srcStride, testByte, testBits, testValue);
235     if(range.GetX() != 0xFFFF)
236     {
237       stretchPixelsY.PushBack(range);
238     }
239   }
240
241   // If there are no stretch pixels then make the entire image stretchable
242   if(stretchPixelsX.Size() == 0)
243   {
244     stretchPixelsX.PushBack(Uint16Pair(0, width - 2));
245   }
246   if(stretchPixelsY.Size() == 0)
247   {
248     stretchPixelsY.PushBack(Uint16Pair(0, height - 2));
249   }
250 }
251
252 bool IsNinePatchUrl(const std::string& url)
253 {
254   bool match = false;
255
256   std::string::const_reverse_iterator iter = url.rbegin();
257   enum
258   {
259     SUFFIX,
260     HASH,
261     HASH_DOT,
262     DONE
263   } state = SUFFIX;
264   while(iter < url.rend())
265   {
266     switch(state)
267     {
268       case SUFFIX:
269       {
270         if(*iter == '.')
271         {
272           state = HASH;
273         }
274         else if(!isalnum(*iter))
275         {
276           state = DONE;
277         }
278       }
279       break;
280       case HASH:
281       {
282         if(*iter == '#' || *iter == '9')
283         {
284           state = HASH_DOT;
285         }
286         else
287         {
288           state = DONE;
289         }
290       }
291       break;
292       case HASH_DOT:
293       {
294         if(*iter == '.')
295         {
296           match = true;
297         }
298         state = DONE; // Stop testing characters
299       }
300       break;
301       case DONE:
302       {
303       }
304       break;
305     }
306
307     // Satisfy prevent
308     if(state == DONE)
309     {
310       break;
311     }
312
313     ++iter;
314   }
315   return match;
316 }
317
318 } // namespace NPatchUtility
319
320 } // namespace Toolkit
321
322 } // namespace Dali