2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/imaging/common/loader-astc.h>
23 #include <dali/public-api/common/compile-time-assert.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/images/pixel.h>
26 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
27 #include <dali/internal/imaging/common/pixel-buffer-impl.h>
29 #include <dali/internal/system/common/file-closer.h>
31 using namespace Dali::Internal::Platform;
35 namespace TizenPlatform
40 // Max width or height of an image.
41 const unsigned MAX_TEXTURE_DIMENSION = 4096;
42 // Max bytes of image data allowed. Not a precise number, just a sanity check.
43 const unsigned MAX_IMAGE_DATA_SIZE = MAX_TEXTURE_DIMENSION * MAX_TEXTURE_DIMENSION;
45 // Minimum and maximum possible sizes for ASTC blocks.
46 const unsigned int MINIMUM_ASTC_BLOCK_SIZE = 4;
47 const unsigned int MAXIMUM_ASTC_BLOCK_SIZE = 12;
51 // This bytes identify an ASTC native file.
52 const Byte FileIdentifier[] = {
53 0x13, 0xAB, 0xA1, 0x5C
58 * @brief This struct defines the ASTC file header values. From ASTC specifications.
59 * Packed attribute stops the structure from being aligned to compiler defaults
60 * so we can be sure of reading the whole header from file in one call to fread().
61 * Note: members to not conform to coding standards in order to be consistent with ASTC spec.
65 unsigned char magic[ 4 ];
66 unsigned char blockdim_x;
67 unsigned char blockdim_y;
68 unsigned char blockdim_z;
69 unsigned char xsize[ 3 ];
70 unsigned char ysize[ 3 ];
71 unsigned char zsize[ 3 ];
72 } __attribute__ ( (__packed__));
74 using namespace Pixel;
77 * @brief This table allows fast conversion from an ASTC block size ([height][width]) to a pixel format.
78 * This could be done within a switch, but this way we have a constant time function.
79 * Note: As 4 is the minimum block size, 4 is subtracted from both the width and height to optimise size.
80 * IE. Table format is: Increasing order of block width from left-to-right: 4 -> 12
81 * Increasing order of block height from top-to-bottom: 4 -> 12
83 Pixel::Format AstcLinearBlockSizeToPixelFormatTable[][( MAXIMUM_ASTC_BLOCK_SIZE - MINIMUM_ASTC_BLOCK_SIZE ) + 1] = {
84 { COMPRESSED_RGBA_ASTC_4x4_KHR, COMPRESSED_RGBA_ASTC_5x4_KHR, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID },
85 { 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 },
86 { INVALID, INVALID, COMPRESSED_RGBA_ASTC_6x6_KHR, INVALID, COMPRESSED_RGBA_ASTC_8x6_KHR, INVALID, COMPRESSED_RGBA_ASTC_10x6_KHR, INVALID, INVALID },
87 { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID },
88 { INVALID, INVALID, INVALID, INVALID, COMPRESSED_RGBA_ASTC_8x8_KHR, INVALID, COMPRESSED_RGBA_ASTC_10x8_KHR, INVALID, INVALID },
89 { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID },
90 { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, COMPRESSED_RGBA_ASTC_10x10_KHR, INVALID, COMPRESSED_RGBA_ASTC_12x10_KHR },
91 { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID },
92 { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, COMPRESSED_RGBA_ASTC_12x12_KHR }
96 * @brief Uses header information to return the respective ASTC pixel format.
98 * @param[in] header A populated AstcFileHeader struct
99 * @return The pixel format, or INVALID if the block size was invalid
101 Pixel::Format GetAstcPixelFormat( AstcFileHeader& header )
103 // Check the block size is valid. This will also prevent an invalid read from the conversion table.
104 if( ( header.blockdim_x < MINIMUM_ASTC_BLOCK_SIZE ) || ( header.blockdim_x > MAXIMUM_ASTC_BLOCK_SIZE ) ||
105 ( header.blockdim_y < MINIMUM_ASTC_BLOCK_SIZE ) || ( header.blockdim_y > MAXIMUM_ASTC_BLOCK_SIZE ) )
107 return Pixel::INVALID;
110 // Read the equivalent pixel format from the conversion table.
111 return AstcLinearBlockSizeToPixelFormatTable[ header.blockdim_y - MINIMUM_ASTC_BLOCK_SIZE ][ header.blockdim_x - MINIMUM_ASTC_BLOCK_SIZE ];
115 * @brief Internal method to load ASTC header info from a file.
117 * @param[in] filePointer The file pointer to the ASTC file to read
118 * @param[out] width The width is output to this value
119 * @param[out] height The height is output to this value
120 * @param[out] fileHeader This will be populated with the header data
121 * @return True if the file is valid, false otherwise
123 bool LoadAstcHeader( FILE * const filePointer, unsigned int& width, unsigned int& height, AstcFileHeader& fileHeader )
125 // Pull the bytes of the file header in as a block:
126 const unsigned int readLength = sizeof( AstcFileHeader );
127 if( InternalFile::fread( &fileHeader, 1, readLength, filePointer ) != readLength )
132 // Check the header contains the ASTC native file identifier.
133 bool headerIsValid = memcmp( fileHeader.magic, FileIdentifier, sizeof( fileHeader.magic ) ) == 0;
136 DALI_LOG_ERROR( "File is not a valid ASTC native file\n" );
137 // Return here as otherwise, if not a valid ASTC file, we are likely to pick up other header errors spuriously.
141 // Convert the 3-byte values for width and height to a single resultant value.
142 width = fileHeader.xsize[0] | ( fileHeader.xsize[1] << 8 ) | ( fileHeader.xsize[2] << 16 );
143 height = fileHeader.ysize[0] | ( fileHeader.ysize[1] << 8 ) | ( fileHeader.ysize[2] << 16 );
145 const unsigned int zDepth = fileHeader.zsize[0] + ( fileHeader.zsize[1] << 8 ) + ( fileHeader.zsize[2] << 16 );
147 // Check image dimensions are within limits.
148 if( ( width > MAX_TEXTURE_DIMENSION ) || ( height > MAX_TEXTURE_DIMENSION ) )
150 DALI_LOG_ERROR( "ASTC file has larger than supported dimensions: %d,%d\n", width, height );
151 headerIsValid = false;
154 // Confirm the ASTC block does not have any Z depth.
157 DALI_LOG_ERROR( "ASTC files with z size other than 1 are not supported. Z size is: %d\n", zDepth );
158 headerIsValid = false;
161 return headerIsValid;
164 } // Unnamed namespace.
167 // File loading API entry-point:
168 bool LoadAstcHeader( const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height )
170 AstcFileHeader fileHeader;
171 return LoadAstcHeader( input.file, width, height, fileHeader );
174 // File loading API entry-point:
175 bool LoadBitmapFromAstc( const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap )
177 FILE* const filePointer = input.file;
180 DALI_LOG_ERROR( "Null file handle passed to ASTC compressed bitmap file loader.\n" );
184 // Load the header info.
185 AstcFileHeader fileHeader;
186 unsigned int width, height;
188 if( !LoadAstcHeader( filePointer, width, height, fileHeader ) )
190 DALI_LOG_ERROR( "Could not load ASTC Header from file.\n" );
194 // Retrieve the pixel format from the ASTC block size.
195 Pixel::Format pixelFormat = GetAstcPixelFormat( fileHeader );
196 if( pixelFormat == Pixel::INVALID )
198 DALI_LOG_ERROR( "No internal pixel format supported for ASTC file pixel format.\n" );
202 // Retrieve the file size.
203 if( InternalFile::fseek( filePointer, 0L, SEEK_END ) )
205 DALI_LOG_ERROR( "Could not seek through file.\n" );
209 off_t fileSize = InternalFile::ftell( filePointer );
210 if( fileSize == -1L )
212 DALI_LOG_ERROR( "Could not determine ASTC file size.\n" );
216 if( InternalFile::fseek( filePointer, sizeof( AstcFileHeader ), SEEK_SET ) )
218 DALI_LOG_ERROR( "Could not seek through file.\n" );
222 // Data size is file size - header size.
223 size_t imageByteCount = fileSize - sizeof( AstcFileHeader );
225 // Sanity-check the image data is not too large and that it is at less than 2 bytes per texel:
226 if( ( imageByteCount > MAX_IMAGE_DATA_SIZE ) || ( imageByteCount > ( ( static_cast< size_t >( width ) * height ) << 1 ) ) )
228 DALI_LOG_ERROR( "ASTC file has too large image-data field.\n" );
232 // allocate pixel data
233 bitmap = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
235 // Compressed format won't allocate the buffer
236 auto pixels = bitmap.GetBuffer();
239 // allocate buffer manually
240 auto& impl = GetImplementation( bitmap );
241 impl.AllocateFixedSize( imageByteCount );
242 pixels = bitmap.GetBuffer();
245 // Load the image data.
246 const size_t bytesRead = InternalFile::fread( pixels, 1, imageByteCount, filePointer );
248 // Check the size of loaded data is what we expected.
249 if( bytesRead != imageByteCount )
251 DALI_LOG_ERROR( "Read of image pixel data failed.\n" );
258 } // namespace TizenPlatform