[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / ktx-loader.cpp
1 /*
2  * Copyright (c) 2024 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 // FILE HEADER
19 #include <dali-scene3d/public-api/loader/ktx-loader.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/pixel-data-integ.h>
23 #include <dali/public-api/rendering/texture.h>
24 #include <fstream>
25 #include <memory>
26
27 namespace Dali
28 {
29 namespace
30 {
31 // http://github.khronos.org/KTX-Specification/
32 const uint8_t KTX_ID_HEAD[] = {0xAB, 0x4B, 0x54, 0x58, 0x20};
33 const uint8_t KTX_ID_TAIL[] = {0xBB, 0x0D, 0x0A, 0x1A, 0x0A};
34
35 const uint8_t KTX_VERSION_1_1[] = {0x31, 0x31};
36 const uint8_t KTX_VERSION_2_0[] = {0x32, 0x30};
37
38 static_assert(sizeof(KTX_ID_HEAD) + sizeof(KTX_ID_TAIL) == 10);
39 static_assert(sizeof(KTX_VERSION_1_1) == 2);
40 static_assert(sizeof(KTX_VERSION_2_0) == sizeof(KTX_VERSION_1_1));
41
42 void FreeBuffer(uint8_t* buffer)
43 {
44   delete[] buffer;
45 }
46 } // namespace
47
48 namespace Scene3D
49 {
50 namespace Loader
51 {
52 struct KtxFileHeader
53 {
54   uint8_t  identifier[12];
55   uint32_t endianness;
56   uint32_t glType; //(UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5, etc.)
57   uint32_t glTypeSize;
58   uint32_t glFormat;         //(RGB, RGBA, BGRA, etc.)
59   uint32_t glInternalFormat; //For uncompressed textures, specifies the internalformat parameter passed to glTexStorage*D or glTexImage*D
60   uint32_t glBaseInternalFormat;
61   uint32_t pixelWidth;
62   uint32_t pixelHeight;
63   uint32_t pixelDepth;
64   uint32_t numberOfArrayElements;
65   uint32_t numberOfFaces; //Cube map faces are stored in the order: +X, -X, +Y, -Y, +Z, -Z.
66   uint32_t numberOfMipmapLevels;
67   uint32_t bytesOfKeyValueData;
68
69   bool IsIdentifierValid() const
70   {
71     return std::equal(KTX_ID_HEAD, std::end(KTX_ID_HEAD), identifier) &&
72            (std::equal(KTX_VERSION_1_1, std::end(KTX_VERSION_1_1), identifier + sizeof(KTX_ID_HEAD)) ||
73             std::equal(KTX_VERSION_2_0, std::end(KTX_VERSION_2_0), identifier + sizeof(KTX_ID_HEAD))) &&
74            std::equal(KTX_ID_TAIL, std::end(KTX_ID_TAIL), identifier + (sizeof(KTX_ID_HEAD) + sizeof(KTX_VERSION_1_1)));
75   }
76 };
77
78 /**
79  * Convert KTX format to Pixel::Format
80  */
81 bool ConvertPixelFormat(const uint32_t ktxPixelFormat, Pixel::Format& format)
82 {
83   switch(ktxPixelFormat)
84   {
85     case 0x93B0: // GL_COMPRESSED_RGBA_ASTC_4x4
86     {
87       format = Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR;
88       break;
89     }
90     case 0x93B1: // GL_COMPRESSED_RGBA_ASTC_5x4
91     {
92       format = Pixel::COMPRESSED_RGBA_ASTC_5x4_KHR;
93       break;
94     }
95     case 0x93B2: // GL_COMPRESSED_RGBA_ASTC_5x5
96     {
97       format = Pixel::COMPRESSED_RGBA_ASTC_5x5_KHR;
98       break;
99     }
100     case 0x93B3: // GL_COMPRESSED_RGBA_ASTC_6x5
101     {
102       format = Pixel::COMPRESSED_RGBA_ASTC_6x5_KHR;
103       break;
104     }
105     case 0x93B4: // GL_COMPRESSED_RGBA_ASTC_6x6
106     {
107       format = Pixel::COMPRESSED_RGBA_ASTC_6x6_KHR;
108       break;
109     }
110     case 0x93B5: // GL_COMPRESSED_RGBA_ASTC_8x5
111     {
112       format = Pixel::COMPRESSED_RGBA_ASTC_8x5_KHR;
113       break;
114     }
115     case 0x93B6: // GL_COMPRESSED_RGBA_ASTC_8x6
116     {
117       format = Pixel::COMPRESSED_RGBA_ASTC_8x6_KHR;
118       break;
119     }
120     case 0x93B7: // GL_COMPRESSED_RGBA_ASTC_8x8
121     {
122       format = Pixel::COMPRESSED_RGBA_ASTC_8x8_KHR;
123       break;
124     }
125     case 0x93B8: // GL_COMPRESSED_RGBA_ASTC_10x5
126     {
127       format = Pixel::COMPRESSED_RGBA_ASTC_10x5_KHR;
128       break;
129     }
130     case 0x93B9: // GL_COMPRESSED_RGBA_ASTC_10x6
131     {
132       format = Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR;
133       break;
134     }
135     case 0x93BA: // GL_COMPRESSED_RGBA_ASTC_10x8
136     {
137       format = Pixel::COMPRESSED_RGBA_ASTC_10x8_KHR;
138       break;
139     }
140     case 0x93BB: // GL_COMPRESSED_RGBA_ASTC_10x10
141     {
142       format = Pixel::COMPRESSED_RGBA_ASTC_10x10_KHR;
143       break;
144     }
145     case 0x93BC: // GL_COMPRESSED_RGBA_ASTC_12x10
146     {
147       format = Pixel::COMPRESSED_RGBA_ASTC_12x10_KHR;
148       break;
149     }
150     case 0x93BD: // GL_COMPRESSED_RGBA_ASTC_12x12
151     {
152       format = Pixel::COMPRESSED_RGBA_ASTC_12x12_KHR;
153       break;
154     }
155     case 0x881B: // GL_RGB16F
156     {
157       format = Pixel::RGB16F;
158       break;
159     }
160     case 0x8815: // GL_RGB32F
161     {
162       format = Pixel::RGB32F;
163       break;
164     }
165     case 0x8C3A: // GL_R11F_G11F_B10F
166     {
167       format = Pixel::R11G11B10F;
168       break;
169     }
170     case 0x8D7C: // GL_RGBA8UI
171     {
172       format = Pixel::RGBA8888;
173       break;
174     }
175     case 0x8D7D: // GL_RGB8UI
176     {
177       format = Pixel::RGB888;
178       break;
179     }
180     default:
181     {
182       return false;
183     }
184   }
185
186   return true;
187 }
188
189 bool LoadKtxData(const std::string& path, EnvironmentMapData& environmentMapData)
190 {
191   std::fstream fp(path, std::ios::in | std::ios::binary);
192   if(fp.is_open() == false)
193   {
194     return false;
195   }
196
197   KtxFileHeader header;
198   if(fp.read(reinterpret_cast<char*>(&header), sizeof(KtxFileHeader)).good() == false)
199   {
200     return false;
201   }
202
203   if(!header.IsIdentifierValid())
204   {
205     return false;
206   }
207
208   // Skip the key-values:
209   if(fp.seekg(static_cast<std::streamoff>(static_cast<std::size_t>(header.bytesOfKeyValueData)), fp.cur).good() == false)
210   {
211     return false;
212   }
213
214   header.numberOfMipmapLevels  = std::max(header.numberOfMipmapLevels, 1u);
215   header.numberOfArrayElements = std::max(header.numberOfArrayElements, 1u);
216   header.pixelDepth            = std::max(header.pixelDepth, 1u);
217   header.pixelHeight           = std::max(header.pixelHeight, 1u);
218   environmentMapData.SetMipmapLevels(header.numberOfMipmapLevels);
219
220   environmentMapData.mPixelData.resize(header.numberOfFaces);
221   for(uint32_t face = 0u; face < header.numberOfFaces; ++face)
222   {
223     environmentMapData.mPixelData[face].resize(header.numberOfMipmapLevels);
224   }
225
226   Pixel::Format daliformat = Pixel::RGB888;
227
228   ConvertPixelFormat(header.glInternalFormat, daliformat);
229
230   for(uint32_t mipmapLevel = 0u; mipmapLevel < header.numberOfMipmapLevels; ++mipmapLevel)
231   {
232     uint32_t byteSize = 0u;
233     if(fp.read(reinterpret_cast<char*>(&byteSize), sizeof(byteSize)).good() == false)
234     {
235       return false;
236     }
237
238     if(0u != byteSize % 4u)
239     {
240       byteSize += 4u - byteSize % 4u;
241     }
242
243     for(uint32_t arrayElement = 0u; arrayElement < header.numberOfArrayElements; ++arrayElement) //arrayElement must be 0 or 1
244     {
245       for(uint32_t face = 0u; face < header.numberOfFaces; ++face)
246       {
247         std::unique_ptr<uint8_t, void (*)(uint8_t*)> img(new uint8_t[byteSize], FreeBuffer);
248         if(fp.read(reinterpret_cast<char*>(img.get()), static_cast<std::streamsize>(static_cast<size_t>(byteSize))).good() == false)
249         {
250           return false;
251         }
252         environmentMapData.mPixelData[face][mipmapLevel] = Dali::Integration::NewPixelDataWithReleaseAfterUpload(img.release(), byteSize, header.pixelWidth, header.pixelHeight, 0u, daliformat, PixelData::DELETE_ARRAY);
253       }
254     }
255
256     header.pixelHeight /= 2u;
257     header.pixelWidth /= 2u;
258   }
259   environmentMapData.SetEnvironmentMapType(Scene3D::EnvironmentMapType::CUBEMAP);
260
261   return true;
262 }
263
264 } // namespace Loader
265 } // namespace Scene3D
266 } // namespace Dali