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