[dali_2.0.27] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / utility / npatch-utilities.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/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     {
179       DALI_LOG_ERROR("Pixel format not compatible.\n");
180       byteOffset = 0;
181       bitMask    = 0;
182       break;
183     }
184   }
185 }
186
187 void ParseBorders(Devel::PixelBuffer& pixelBuffer, StretchRanges& stretchPixelsX, StretchRanges& stretchPixelsY)
188 {
189   stretchPixelsX.Clear();
190   stretchPixelsY.Clear();
191
192   Pixel::Format pixelFormat = pixelBuffer.GetPixelFormat();
193
194   int32_t alphaByte = 0;
195   int32_t alphaBits = 0;
196   Pixel::GetAlphaOffsetAndMask(pixelFormat, alphaByte, alphaBits);
197
198   int32_t testByte  = alphaByte;
199   int32_t testBits  = alphaBits;
200   int32_t testValue = alphaBits; // Opaque == stretch
201   if(!alphaBits)
202   {
203     GetRedOffsetAndMask(pixelFormat, testByte, testBits);
204     testValue = 0; // Black == stretch
205   }
206
207   uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(pixelFormat);
208   uint32_t width         = pixelBuffer.GetWidth();
209   uint32_t height        = pixelBuffer.GetHeight();
210   uint8_t* srcPixels     = pixelBuffer.GetBuffer();
211   uint32_t srcStride     = width * bytesPerPixel;
212
213   // TOP
214   uint8_t* top   = srcPixels + bytesPerPixel;
215   uint32_t index = 0;
216
217   for(; index < width - 2;)
218   {
219     Uint16Pair range = ParseRange(index, width - 2, top, bytesPerPixel, testByte, testBits, testValue);
220     if(range.GetX() != 0xFFFF)
221     {
222       stretchPixelsX.PushBack(range);
223     }
224   }
225
226   // LEFT
227   uint8_t* left = srcPixels + srcStride;
228   index         = 0;
229   for(; index < height - 2;)
230   {
231     Uint16Pair range = ParseRange(index, height - 2, left, srcStride, testByte, testBits, testValue);
232     if(range.GetX() != 0xFFFF)
233     {
234       stretchPixelsY.PushBack(range);
235     }
236   }
237
238   // If there are no stretch pixels then make the entire image stretchable
239   if(stretchPixelsX.Size() == 0)
240   {
241     stretchPixelsX.PushBack(Uint16Pair(0, width - 2));
242   }
243   if(stretchPixelsY.Size() == 0)
244   {
245     stretchPixelsY.PushBack(Uint16Pair(0, height - 2));
246   }
247 }
248
249 bool IsNinePatchUrl(const std::string& url)
250 {
251   bool match = false;
252
253   std::string::const_reverse_iterator iter = url.rbegin();
254   enum
255   {
256     SUFFIX,
257     HASH,
258     HASH_DOT,
259     DONE
260   } state = SUFFIX;
261   while(iter < url.rend())
262   {
263     switch(state)
264     {
265       case SUFFIX:
266       {
267         if(*iter == '.')
268         {
269           state = HASH;
270         }
271         else if(!isalnum(*iter))
272         {
273           state = DONE;
274         }
275       }
276       break;
277       case HASH:
278       {
279         if(*iter == '#' || *iter == '9')
280         {
281           state = HASH_DOT;
282         }
283         else
284         {
285           state = DONE;
286         }
287       }
288       break;
289       case HASH_DOT:
290       {
291         if(*iter == '.')
292         {
293           match = true;
294         }
295         state = DONE; // Stop testing characters
296       }
297       break;
298       case DONE:
299       {
300       }
301       break;
302     }
303
304     // Satisfy prevent
305     if(state == DONE)
306     {
307       break;
308     }
309
310     ++iter;
311   }
312   return match;
313 }
314
315 } // namespace NPatchUtility
316
317 } // namespace Toolkit
318
319 } // namespace Dali