[Tizen] Add Pixel format for depth and stencil
[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 // EXTERNAL HEADER
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
24 #include <dali/devel-api/common/hash.h>
25 #include <dali/integration-api/debug.h>
26
27 namespace Dali
28 {
29
30 namespace Toolkit
31 {
32
33 namespace Internal
34 {
35
36 namespace NPatchBuffer
37 {
38
39 void GetRedOffsetAndMask( Dali::Pixel::Format pixelFormat, int& byteOffset, int& bitMask )
40 {
41   switch( pixelFormat )
42   {
43     case Dali::Pixel::A8:
44     case Dali::Pixel::L8:
45     case Dali::Pixel::LA88:
46     {
47       byteOffset = 0;
48       bitMask = 0;
49       break;
50     }
51     case Dali::Pixel::RGB888:
52     case Dali::Pixel::RGB8888:
53     case Dali::Pixel::RGBA8888:
54     {
55       byteOffset = 0;
56       bitMask = 0xFF;
57       break;
58     }
59     case Dali::Pixel::BGR8888:
60     case Dali::Pixel::BGRA8888:
61     {
62       byteOffset = 2;
63       bitMask = 0xff;
64       break;
65     }
66     case Dali::Pixel::RGB565:
67     {
68       byteOffset = 0;
69       bitMask = 0xf8;
70       break;
71     }
72     case Dali::Pixel::BGR565:
73     {
74       byteOffset = 1;
75       bitMask = 0x1f;
76       break;
77     }
78     case Dali::Pixel::RGBA4444:
79     {
80       byteOffset = 0;
81       bitMask = 0xf0;
82       break;
83     }
84     case Dali::Pixel::BGRA4444:
85     {
86       byteOffset = 1;
87       bitMask = 0xf0;
88       break;
89     }
90     case Dali::Pixel::RGBA5551:
91     {
92       byteOffset = 0;
93       bitMask = 0xf8;
94       break;
95     }
96     case Dali::Pixel::BGRA5551:
97     {
98       byteOffset = 1;
99       bitMask = 0x1e;
100       break;
101     }
102     case Dali::Pixel::INVALID:
103     case Dali::Pixel::COMPRESSED_R11_EAC:
104     case Dali::Pixel::COMPRESSED_SIGNED_R11_EAC:
105     case Dali::Pixel::COMPRESSED_RG11_EAC:
106     case Dali::Pixel::COMPRESSED_SIGNED_RG11_EAC:
107     case Dali::Pixel::COMPRESSED_RGB8_ETC2:
108     case Dali::Pixel::COMPRESSED_SRGB8_ETC2:
109     case Dali::Pixel::COMPRESSED_RGB8_ETC1:
110     case Dali::Pixel::COMPRESSED_RGB_PVRTC_4BPPV1:
111     case Dali::Pixel::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
112     case Dali::Pixel::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
113     case Dali::Pixel::COMPRESSED_RGBA8_ETC2_EAC:
114     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
115     case Dali::Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR:
116     case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x4_KHR:
117     case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x5_KHR:
118     case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x5_KHR:
119     case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x6_KHR:
120     case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x5_KHR:
121     case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x6_KHR:
122     case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x8_KHR:
123     case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x5_KHR:
124     case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR:
125     case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x8_KHR:
126     case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x10_KHR:
127     case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x10_KHR:
128     case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x12_KHR:
129     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
130     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
131     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
132     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
133     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
134     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
135     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
136     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
137     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
138     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
139     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
140     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
141     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
142     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
143     {
144       DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple masking-out of per-pixel alpha.\n");
145       byteOffset=0;
146       bitMask=0;
147       break;
148     }
149     case Dali::Pixel::RGB16F:
150     case Dali::Pixel::RGB32F:
151     case Dali::Pixel::DEPTH_UNSIGNED_INT:
152     case Dali::Pixel::DEPTH_FLOAT:
153     case Dali::Pixel::DEPTH_STENCIL:
154     {
155       DALI_LOG_ERROR("Pixel format not compatible.\n");
156       byteOffset=0;
157       bitMask=0;
158       break;
159     }
160   }
161 }
162
163 Uint16Pair ParseRange( unsigned int& index, unsigned int width, unsigned char*& pixel, unsigned int pixelStride, int testByte, int testBits, int testValue )
164 {
165   unsigned int start = 0xFFFF;
166   for( ; index < width; ++index, pixel += pixelStride )
167   {
168     if( ( pixel[ testByte ] & testBits ) == testValue )
169     {
170         start = index;
171         ++index;
172         pixel += pixelStride;
173         break;
174     }
175   }
176
177   unsigned int end = width;
178   for( ; index < width; ++index, pixel += pixelStride )
179   {
180     if( ( pixel[ testByte ] & testBits ) != testValue )
181     {
182         end = index;
183         ++index;
184         pixel += pixelStride;
185         break;
186     }
187   }
188
189   return Uint16Pair( start, end );
190 }
191
192 void ParseBorders( Devel::PixelBuffer& pixelBuffer, NPatchLoader::Data* data  )
193 {
194   data->stretchPixelsX.Clear();
195   data->stretchPixelsY.Clear();
196
197   Pixel::Format pixelFormat = pixelBuffer.GetPixelFormat();
198
199   int alphaByte = 0;
200   int alphaBits = 0;
201   Pixel::GetAlphaOffsetAndMask( pixelFormat, alphaByte, alphaBits );
202
203   int testByte = alphaByte;
204   int testBits = alphaBits;
205   int testValue = alphaBits; // Opaque == stretch
206   if( !alphaBits )
207   {
208     GetRedOffsetAndMask( pixelFormat, testByte, testBits );
209     testValue = 0;           // Black == stretch
210   }
211
212   unsigned int bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat );
213   unsigned int width = pixelBuffer.GetWidth();
214   unsigned int height = pixelBuffer.GetHeight();
215   unsigned char* srcPixels = pixelBuffer.GetBuffer();
216   unsigned int srcStride = width * bytesPerPixel;
217
218   // TOP
219   unsigned char* top = srcPixels + bytesPerPixel;
220   unsigned int index = 0;
221
222   for( ; index < width - 2; )
223   {
224     Uint16Pair range = ParseRange( index, width - 2, top, bytesPerPixel, testByte, testBits, testValue );
225     if( range.GetX() != 0xFFFF )
226     {
227       data->stretchPixelsX.PushBack( range );
228     }
229   }
230
231   // LEFT
232   unsigned char* left  = srcPixels + srcStride;
233   index = 0;
234   for( ; index < height - 2; )
235   {
236     Uint16Pair range = ParseRange( index, height - 2, left, srcStride, testByte, testBits, testValue );
237     if( range.GetX() != 0xFFFF )
238     {
239       data->stretchPixelsY.PushBack( range );
240     }
241   }
242
243   // If there are no stretch pixels then make the entire image stretchable
244   if( data->stretchPixelsX.Size() == 0 )
245   {
246     data->stretchPixelsX.PushBack( Uint16Pair( 0, width - 2 ) );
247   }
248   if( data->stretchPixelsY.Size() == 0 )
249   {
250     data->stretchPixelsY.PushBack( Uint16Pair( 0, height - 2 ) );
251   }
252 }
253
254 } // namespace NPatchBuffer
255
256 NPatchLoader::NPatchLoader()
257 {
258 }
259
260 NPatchLoader::~NPatchLoader()
261 {
262 }
263
264 std::size_t NPatchLoader::Load( const std::string& url, const Rect< int >& border, bool& preMultiplyOnLoad )
265 {
266   std::size_t hash = CalculateHash( url );
267   OwnerContainer< Data* >::SizeType index = UNINITIALIZED_ID;
268   const OwnerContainer< Data* >::SizeType count = mCache.Count();
269   int cachedIndex = -1;
270
271   for( ; index < count; ++index )
272   {
273     if( mCache[ index ]->hash == hash )
274     {
275       // hash match, check url as well in case of hash collision
276       if( mCache[ index ]->url == url )
277       {
278         // Use cached data
279         if( mCache[ index ]->border == border )
280         {
281           return index+1u; // valid indices are from 1 onwards
282         }
283         else
284         {
285           cachedIndex = index;
286         }
287       }
288     }
289   }
290
291   if( cachedIndex != -1 )
292   {
293     // Same url but border is different - use the existing texture
294     Data* data = new Data();
295     data->hash = hash;
296     data->url = url;
297     data->croppedWidth = mCache[ cachedIndex ]->croppedWidth;
298     data->croppedHeight = mCache[ cachedIndex ]->croppedHeight;
299
300     data->textureSet = mCache[ cachedIndex ]->textureSet;
301
302     StretchRanges stretchRangesX;
303     stretchRangesX.PushBack( Uint16Pair( border.left, ( (data->croppedWidth >= static_cast< unsigned int >( border.right )) ? data->croppedWidth - border.right : 0 ) ) );
304
305     StretchRanges stretchRangesY;
306     stretchRangesY.PushBack( Uint16Pair( border.top, ( (data->croppedHeight >= static_cast< unsigned int >( border.bottom )) ? data->croppedHeight - border.bottom : 0 ) ) );
307
308     data->stretchPixelsX = stretchRangesX;
309     data->stretchPixelsY = stretchRangesY;
310     data->border = border;
311
312     mCache.PushBack( data );
313
314     return mCache.Count(); // valid ids start from 1u
315   }
316
317   // got to the end so no match, decode N patch and append new item to cache
318   Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromFile( url, ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true );
319   if( pixelBuffer )
320   {
321     Data* data = new Data();
322     data->hash = hash;
323     data->url = url;
324
325     if( border == Rect< int >( 0, 0, 0, 0 ) )
326     {
327       NPatchBuffer::ParseBorders( pixelBuffer, data );
328
329       data->border = Rect< int >( 0, 0, 0, 0 );
330
331       // Crop the image
332       pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 );
333     }
334     else
335     {
336       data->stretchPixelsX.PushBack( Uint16Pair( border.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( border.right )) ? pixelBuffer.GetWidth() - border.right : 0 ) ) );
337       data->stretchPixelsY.PushBack( Uint16Pair( border.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( border.bottom )) ? pixelBuffer.GetHeight() - border.bottom : 0 ) ) );
338       data->border = border;
339     }
340
341     data->croppedWidth = pixelBuffer.GetWidth();
342     data->croppedHeight = pixelBuffer.GetHeight();
343
344     if( preMultiplyOnLoad && Pixel::HasAlpha( pixelBuffer.GetPixelFormat() ) )
345     {
346       pixelBuffer.MultiplyColorByAlpha();
347     }
348     else
349     {
350       preMultiplyOnLoad = false;
351     }
352
353     PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer
354
355     Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
356     texture.Upload( pixels );
357
358     data->textureSet = TextureSet::New();
359     data->textureSet.SetTexture( 0u, texture );
360
361     mCache.PushBack( data );
362
363     return mCache.Count(); // valid ids start from 1u
364   }
365
366   return 0u;
367 }
368
369 bool NPatchLoader::GetNPatchData( std::size_t id, const Data*& data )
370 {
371   if( ( id > UNINITIALIZED_ID )&&( id <= mCache.Count() ) )
372   {
373     data = mCache[ id - 1u ]; // id's start from 1u
374     return true;
375   }
376   data = NULL;
377   return false;
378 }
379
380 } // namespace Internal
381
382 } // namespace Toolkit
383
384 } // namespace Dali