[dali_2.0.7] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene-loader / public-api / ktx-loader.cpp
1 /*
2  * Copyright (c) 2020 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 "dali/public-api/rendering/texture.h"
23 #include <fstream>
24 #include <memory>
25
26 namespace Dali
27 {
28
29 namespace
30 {
31
32 // http://github.khronos.org/KTX-Specification/
33 const uint8_t KTX_ID_HEAD[] = { 0xAB, 0x4B, 0x54, 0x58, 0x20 };
34 const uint8_t KTX_ID_TAIL[] = { 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
35
36 const uint8_t KTX_VERSION_1_1[] = { 0x31, 0x31 };
37 const uint8_t KTX_VERSION_2_0[] = { 0x32, 0x30 };
38
39 static_assert(sizeof(KTX_ID_HEAD) + sizeof(KTX_ID_TAIL) == 10);
40 static_assert(sizeof(KTX_VERSION_1_1) == 2);
41 static_assert(sizeof(KTX_VERSION_2_0) == sizeof(KTX_VERSION_1_1));
42
43 void FreeBuffer(uint8_t* buffer)
44 {
45   delete[] buffer;
46 }
47 } // namespace
48
49 namespace SceneLoader
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::RGB32F;
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 Texture CubeData::CreateTexture() const
189 {
190   Texture texture = Texture::New(TextureType::TEXTURE_CUBE, data[0][0].GetPixelFormat(),
191     data[0][0].GetWidth(), data[0][0].GetHeight());
192   for (size_t iSide = 0u, iEndSize = data.size(); iSide < iEndSize; ++iSide)
193   {
194     auto& side = data[iSide];
195     for (size_t iMipLevel = 0u, iEndMipLevel = data[0].size(); iMipLevel < iEndMipLevel; ++iMipLevel)
196     {
197       texture.Upload(side[iMipLevel], CubeMapLayer::POSITIVE_X + iSide, iMipLevel,
198         0u, 0u, side[iMipLevel].GetWidth(), side[iMipLevel].GetHeight());
199     }
200   }
201
202   return texture;
203 }
204
205 bool LoadCubeMapData(const std::string& path, CubeData& cubedata)
206 {
207   std::fstream fp(path, std::ios::in | std::ios::binary);
208   if (fp.is_open() == false)
209   {
210     return false;
211   }
212
213   KtxFileHeader header;
214   if (fp.read(reinterpret_cast<char*>(&header), sizeof(KtxFileHeader)).good() == false)
215   {
216     return false;
217   }
218
219   if (!header.IsIdentifierValid())
220   {
221     return false;
222   }
223
224   // Skip the key-values:
225   if (fp.seekg(header.bytesOfKeyValueData, fp.cur).good() == false)
226   {
227     return false;
228   }
229
230   header.numberOfMipmapLevels = std::max(header.numberOfMipmapLevels, 1u);
231   header.numberOfArrayElements = std::max(header.numberOfArrayElements, 1u);
232   header.pixelDepth = std::max(header.pixelDepth, 1u);
233   header.pixelHeight = std::max(header.pixelHeight, 1u);
234
235   cubedata.data.resize(header.numberOfFaces);
236   for (uint32_t face = 0u; face < header.numberOfFaces; ++face)
237   {
238     cubedata.data[face].resize(header.numberOfMipmapLevels);
239   }
240
241   Pixel::Format daliformat = Pixel::RGB888;
242
243   ConvertPixelFormat(header.glInternalFormat, daliformat);
244
245   for (uint32_t mipmapLevel = 0u; mipmapLevel < header.numberOfMipmapLevels; ++mipmapLevel)
246   {
247     uint32_t byteSize = 0u;
248     if (fp.read(reinterpret_cast<char*>(&byteSize), sizeof(byteSize)).good() == false)
249     {
250       return false;
251     }
252
253     if (0u != byteSize % 4u)
254     {
255       byteSize += 4u - byteSize % 4u;
256     }
257
258     for (uint32_t arrayElement = 0u; arrayElement < header.numberOfArrayElements; ++arrayElement) //arrayElement must be 0 or 1
259     {
260       for (uint32_t face = 0u; face < header.numberOfFaces; ++face)
261       {
262         std::unique_ptr<uint8_t, void(*)(uint8_t*)>img(new uint8_t[byteSize], FreeBuffer);
263         if (fp.read(reinterpret_cast<char*>(img.get()), byteSize).good() == false)
264         {
265           return false;
266         }
267         cubedata.data[face][mipmapLevel] = PixelData::New(img.release(), byteSize, header.pixelWidth, header.pixelHeight, daliformat, PixelData::DELETE_ARRAY);
268       }
269     }
270
271     header.pixelHeight /= 2u;
272     header.pixelWidth /= 2u;
273   }
274
275   return true;
276 }
277
278 }
279 }