Merge "Added handler for ECORE_WL_EVENT_WINDOW_VISIBILITY_CHANGE" into devel/master
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / tizen / image-loaders / loader-astc.cpp
1 /*
2  * Copyright (c) 2017 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 "loader-astc.h"
20
21 // EXTERNAL INCLUDES
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25 #include <stdint.h>
26 #include <dali/public-api/common/compile-time-assert.h>
27 #include <dali/integration-api/debug.h>
28 #include <dali/integration-api/bitmap.h>
29 #include <dali/public-api/images/pixel.h>
30
31 namespace Dali
32 {
33 using Integration::Bitmap;
34 using Dali::Integration::PixelBuffer;
35
36 namespace TizenPlatform
37 {
38
39 namespace
40 {
41
42 // Max width or height of an image.
43 const unsigned MAX_TEXTURE_DIMENSION = 4096;
44 // Max bytes of image data allowed. Not a precise number, just a sanity check.
45 const unsigned MAX_IMAGE_DATA_SIZE = MAX_TEXTURE_DIMENSION * MAX_TEXTURE_DIMENSION;
46
47 // Minimum and maximum possible sizes for ASTC blocks.
48 const unsigned int MINIMUM_ASTC_BLOCK_SIZE = 4;
49 const unsigned int MAXIMUM_ASTC_BLOCK_SIZE = 12;
50
51 typedef uint8_t Byte;
52
53 // This bytes identify an ASTC native file.
54 const Byte FileIdentifier[] = {
55    0x13, 0xAB, 0xA1, 0x5C
56 };
57
58
59 /**
60  * @brief This struct defines the ASTC file header values. From ASTC specifications.
61  * Packed attribute stops the structure from being aligned to compiler defaults
62  * so we can be sure of reading the whole header from file in one call to fread().
63  * Note: members to not conform to coding standards in order to be consistent with ASTC spec.
64  */
65 struct AstcFileHeader
66 {
67   unsigned char magic[ 4 ];
68   unsigned char blockdim_x;
69   unsigned char blockdim_y;
70   unsigned char blockdim_z;
71   unsigned char xsize[ 3 ];
72   unsigned char ysize[ 3 ];
73   unsigned char zsize[ 3 ];
74 } __attribute__ ( (__packed__));
75
76 using namespace Pixel;
77
78 /**
79  * @brief This table allows fast conversion from an ASTC block size ([height][width]) to a pixel format.
80  * This could be done within a switch, but this way we have a constant time function.
81  * Note: As 4 is the minimum block size, 4 is subtracted from both the width and height to optimise size.
82  * IE. Table format is: Increasing order of block width from left-to-right:  4 -> 12
83  *                      Increasing order of block height from top-to-bottom: 4 -> 12
84  */
85 Pixel::Format AstcLinearBlockSizeToPixelFormatTable[][( MAXIMUM_ASTC_BLOCK_SIZE - MINIMUM_ASTC_BLOCK_SIZE ) + 1] = {
86     { COMPRESSED_RGBA_ASTC_4x4_KHR, COMPRESSED_RGBA_ASTC_5x4_KHR, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID },
87     { INVALID, COMPRESSED_RGBA_ASTC_5x5_KHR, COMPRESSED_RGBA_ASTC_6x5_KHR, INVALID, COMPRESSED_RGBA_ASTC_8x5_KHR, INVALID, COMPRESSED_RGBA_ASTC_10x5_KHR, INVALID, INVALID },
88     { INVALID, INVALID, COMPRESSED_RGBA_ASTC_6x6_KHR, INVALID, COMPRESSED_RGBA_ASTC_8x6_KHR, INVALID, COMPRESSED_RGBA_ASTC_10x6_KHR, INVALID, INVALID },
89     { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID },
90     { INVALID, INVALID, INVALID, INVALID, COMPRESSED_RGBA_ASTC_8x8_KHR, INVALID, COMPRESSED_RGBA_ASTC_10x8_KHR, INVALID, INVALID },
91     { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID },
92     { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, COMPRESSED_RGBA_ASTC_10x10_KHR, INVALID, COMPRESSED_RGBA_ASTC_12x10_KHR },
93     { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID },
94     { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, COMPRESSED_RGBA_ASTC_12x12_KHR }
95 };
96
97 /**
98  * @brief Uses header information to return the respective ASTC pixel format.
99  *
100  * @param[in] header A populated AstcFileHeader struct
101  * @return    The pixel format, or INVALID if the block size was invalid
102  */
103 Pixel::Format GetAstcPixelFormat( AstcFileHeader& header )
104 {
105   // Check the block size is valid. This will also prevent an invalid read from the conversion table.
106   if( ( header.blockdim_x < MINIMUM_ASTC_BLOCK_SIZE ) || ( header.blockdim_x > MAXIMUM_ASTC_BLOCK_SIZE ) ||
107       ( header.blockdim_y < MINIMUM_ASTC_BLOCK_SIZE ) || ( header.blockdim_y > MAXIMUM_ASTC_BLOCK_SIZE ) )
108   {
109     return Pixel::INVALID;
110   }
111
112   // Read the equivalent pixel format from the conversion table.
113   return AstcLinearBlockSizeToPixelFormatTable[ header.blockdim_y - MINIMUM_ASTC_BLOCK_SIZE ][ header.blockdim_x - MINIMUM_ASTC_BLOCK_SIZE ];
114 }
115
116 /**
117  * @brief Internal method to load ASTC header info from a file.
118  *
119  * @param[in]  filePointer The file pointer to the ASTC file to read
120  * @param[out] width       The width is output to this value
121  * @param[out] height      The height is output to this value
122  * @param[out] fileHeader  This will be populated with the header data
123  * @return                 True if the file is valid, false otherwise
124  */
125 bool LoadAstcHeader( FILE * const filePointer, unsigned int& width, unsigned int& height, AstcFileHeader& fileHeader )
126 {
127   // Pull the bytes of the file header in as a block:
128   const unsigned int readLength = sizeof( AstcFileHeader );
129   if( fread( &fileHeader, 1, readLength, filePointer ) != readLength )
130   {
131     return false;
132   }
133
134   // Check the header contains the ASTC native file identifier.
135   bool headerIsValid = memcmp( fileHeader.magic, FileIdentifier, sizeof( fileHeader.magic ) ) == 0;
136   if( !headerIsValid )
137   {
138     DALI_LOG_ERROR( "File is not a valid ASTC native file\n" );
139     // Return here as otherwise, if not a valid ASTC file, we are likely to pick up other header errors spuriously.
140     return false;
141   }
142
143   // Convert the 3-byte values for width and height to a single resultant value.
144   width = fileHeader.xsize[0] | ( fileHeader.xsize[1] << 8 ) | ( fileHeader.xsize[2] << 16 );
145   height = fileHeader.ysize[0] | ( fileHeader.ysize[1] << 8 ) | ( fileHeader.ysize[2] << 16 );
146
147   const unsigned int zDepth = fileHeader.zsize[0] + ( fileHeader.zsize[1] << 8 ) + ( fileHeader.zsize[2] << 16 );
148
149   // Check image dimensions are within limits.
150   if( ( width > MAX_TEXTURE_DIMENSION ) || ( height > MAX_TEXTURE_DIMENSION ) )
151   {
152     DALI_LOG_ERROR( "ASTC file has larger than supported dimensions: %d,%d\n", width, height );
153     headerIsValid = false;
154   }
155
156   // Confirm the ASTC block does not have any Z depth.
157   if( zDepth != 1 )
158   {
159     DALI_LOG_ERROR( "ASTC files with z size other than 1 are not supported. Z size is: %d\n", zDepth );
160     headerIsValid = false;
161   }
162
163   return headerIsValid;
164 }
165
166 } // Unnamed namespace.
167
168
169 // File loading API entry-point:
170 bool LoadAstcHeader( const ImageLoader::Input& input, unsigned int& width, unsigned int& height )
171 {
172   AstcFileHeader fileHeader;
173   return LoadAstcHeader( input.file, width, height, fileHeader );
174 }
175
176 // File loading API entry-point:
177 bool LoadBitmapFromAstc( const ImageLoader::Input& input, Integration::Bitmap& bitmap )
178 {
179   FILE* const filePointer = input.file;
180   if( !filePointer )
181   {
182     DALI_LOG_ERROR( "Null file handle passed to ASTC compressed bitmap file loader.\n" );
183     return false;
184   }
185
186   // Load the header info.
187   AstcFileHeader fileHeader;
188   unsigned int width, height;
189
190   if( !LoadAstcHeader( filePointer, width, height, fileHeader ) )
191   {
192     DALI_LOG_ERROR( "Could not load ASTC Header from file.\n" );
193     return false;
194   }
195
196   // Retrieve the pixel format from the ASTC block size.
197   Pixel::Format pixelFormat = GetAstcPixelFormat( fileHeader );
198   if( pixelFormat == Pixel::INVALID )
199   {
200     DALI_LOG_ERROR( "No internal pixel format supported for ASTC file pixel format.\n" );
201     return false;
202   }
203
204   // Retrieve the file size.
205   if( fseek( filePointer, 0L, SEEK_END ) )
206   {
207     DALI_LOG_ERROR( "Could not seek through file.\n" );
208     return false;
209   }
210
211   off_t fileSize = ftell( filePointer );
212   if( fileSize == -1L )
213   {
214     DALI_LOG_ERROR( "Could not determine ASTC file size.\n" );
215     return false;
216   }
217
218   if( fseek( filePointer, sizeof( AstcFileHeader ), SEEK_SET ) )
219   {
220     DALI_LOG_ERROR( "Could not seek through file.\n" );
221     return false;
222   }
223
224   // Data size is file size - header size.
225   size_t imageByteCount = fileSize - sizeof( AstcFileHeader );
226
227   // Sanity-check the image data is not too large and that it is at less than 2 bytes per texel:
228   if( ( imageByteCount > MAX_IMAGE_DATA_SIZE ) || ( imageByteCount > ( ( static_cast< size_t >( width ) * height ) << 1 ) ) )
229   {
230     DALI_LOG_ERROR( "ASTC file has too large image-data field.\n" );
231     return false;
232   }
233
234   // Allocate space to load the image data in to.
235   PixelBuffer* const pixels = bitmap.GetCompressedProfile()->ReserveBufferOfSize( pixelFormat, width, height, imageByteCount );
236   if( !pixels )
237   {
238     DALI_LOG_ERROR( "Unable to reserve a pixel buffer to load the requested bitmap into.\n" );
239     return false;
240   }
241
242   // Load the image data.
243   const size_t bytesRead = fread( pixels, 1, imageByteCount, filePointer );
244   // Check the size of loaded data is what we expected.
245   if( bytesRead != imageByteCount )
246   {
247     DALI_LOG_ERROR( "Read of image pixel data failed.\n" );
248     return false;
249   }
250
251   return true;
252 }
253
254 } // namespace TizenPlatform
255
256 } // namespace Dali