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