2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "dali-scene-loader/public-api/ktx-loader.h"
24 #include "dali/public-api/rendering/texture.h"
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};
34 const uint8_t KTX_VERSION_1_1[] = {0x31, 0x31};
35 const uint8_t KTX_VERSION_2_0[] = {0x32, 0x30};
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));
41 void FreeBuffer(uint8_t* buffer)
51 uint8_t identifier[12];
53 uint32_t glType; //(UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5, etc.)
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;
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;
66 bool IsIdentifierValid() const
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)));
76 * Convert KTX format to Pixel::Format
78 bool ConvertPixelFormat(const uint32_t ktxPixelFormat, Pixel::Format& format)
80 switch(ktxPixelFormat)
82 case 0x93B0: // GL_COMPRESSED_RGBA_ASTC_4x4
84 format = Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR;
87 case 0x93B1: // GL_COMPRESSED_RGBA_ASTC_5x4
89 format = Pixel::COMPRESSED_RGBA_ASTC_5x4_KHR;
92 case 0x93B2: // GL_COMPRESSED_RGBA_ASTC_5x5
94 format = Pixel::COMPRESSED_RGBA_ASTC_5x5_KHR;
97 case 0x93B3: // GL_COMPRESSED_RGBA_ASTC_6x5
99 format = Pixel::COMPRESSED_RGBA_ASTC_6x5_KHR;
102 case 0x93B4: // GL_COMPRESSED_RGBA_ASTC_6x6
104 format = Pixel::COMPRESSED_RGBA_ASTC_6x6_KHR;
107 case 0x93B5: // GL_COMPRESSED_RGBA_ASTC_8x5
109 format = Pixel::COMPRESSED_RGBA_ASTC_8x5_KHR;
112 case 0x93B6: // GL_COMPRESSED_RGBA_ASTC_8x6
114 format = Pixel::COMPRESSED_RGBA_ASTC_8x6_KHR;
117 case 0x93B7: // GL_COMPRESSED_RGBA_ASTC_8x8
119 format = Pixel::COMPRESSED_RGBA_ASTC_8x8_KHR;
122 case 0x93B8: // GL_COMPRESSED_RGBA_ASTC_10x5
124 format = Pixel::COMPRESSED_RGBA_ASTC_10x5_KHR;
127 case 0x93B9: // GL_COMPRESSED_RGBA_ASTC_10x6
129 format = Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR;
132 case 0x93BA: // GL_COMPRESSED_RGBA_ASTC_10x8
134 format = Pixel::COMPRESSED_RGBA_ASTC_10x8_KHR;
137 case 0x93BB: // GL_COMPRESSED_RGBA_ASTC_10x10
139 format = Pixel::COMPRESSED_RGBA_ASTC_10x10_KHR;
142 case 0x93BC: // GL_COMPRESSED_RGBA_ASTC_12x10
144 format = Pixel::COMPRESSED_RGBA_ASTC_12x10_KHR;
147 case 0x93BD: // GL_COMPRESSED_RGBA_ASTC_12x12
149 format = Pixel::COMPRESSED_RGBA_ASTC_12x12_KHR;
152 case 0x881B: // GL_RGB16F
154 format = Pixel::RGB16F;
157 case 0x8815: // GL_RGB32F
159 format = Pixel::RGB32F;
162 case 0x8C3A: // GL_R11F_G11F_B10F
164 format = Pixel::R11G11B10F;
167 case 0x8D7C: // GL_RGBA8UI
169 format = Pixel::RGBA8888;
172 case 0x8D7D: // GL_RGB8UI
174 format = Pixel::RGB888;
186 Texture CubeData::CreateTexture() const
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)
191 auto& side = data[iSide];
192 for(size_t iMipLevel = 0u, iEndMipLevel = data[0].size(); iMipLevel < iEndMipLevel; ++iMipLevel)
194 texture.Upload(side[iMipLevel], CubeMapLayer::POSITIVE_X + iSide, iMipLevel, 0u, 0u, side[iMipLevel].GetWidth(), side[iMipLevel].GetHeight());
201 bool LoadCubeMapData(const std::string& path, CubeData& cubedata)
203 std::fstream fp(path, std::ios::in | std::ios::binary);
204 if(fp.is_open() == false)
209 KtxFileHeader header;
210 if(fp.read(reinterpret_cast<char*>(&header), sizeof(KtxFileHeader)).good() == false)
215 if(!header.IsIdentifierValid())
220 // Skip the key-values:
221 if(fp.seekg(header.bytesOfKeyValueData, fp.cur).good() == false)
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);
231 cubedata.data.resize(header.numberOfFaces);
232 for(uint32_t face = 0u; face < header.numberOfFaces; ++face)
234 cubedata.data[face].resize(header.numberOfMipmapLevels);
237 Pixel::Format daliformat = Pixel::RGB888;
239 ConvertPixelFormat(header.glInternalFormat, daliformat);
241 for(uint32_t mipmapLevel = 0u; mipmapLevel < header.numberOfMipmapLevels; ++mipmapLevel)
243 uint32_t byteSize = 0u;
244 if(fp.read(reinterpret_cast<char*>(&byteSize), sizeof(byteSize)).good() == false)
249 if(0u != byteSize % 4u)
251 byteSize += 4u - byteSize % 4u;
254 for(uint32_t arrayElement = 0u; arrayElement < header.numberOfArrayElements; ++arrayElement) //arrayElement must be 0 or 1
256 for(uint32_t face = 0u; face < header.numberOfFaces; ++face)
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)
263 cubedata.data[face][mipmapLevel] = PixelData::New(img.release(), byteSize, header.pixelWidth, header.pixelHeight, daliformat, PixelData::DELETE_ARRAY);
267 header.pixelHeight /= 2u;
268 header.pixelWidth /= 2u;
274 } // namespace SceneLoader