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