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