Fixed launch crash because of double file close
[platform/core/uifw/dali-demo.git] / examples / rendering-basic-pbr / ktx-loader.cpp
1 /*
2  * Copyright (c) 2019 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 "ktx-loader.h"
20
21 // EXTERNAL INCLUDES
22 #include <memory.h>
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/devel-api/adaptor-framework/file-stream.h>
27
28 namespace PbrDemo
29 {
30
31 struct KtxFileHeader
32 {
33   char   identifier[12];
34   uint32_t  endianness;
35   uint32_t  glType;    //(UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5, etc.)
36   uint32_t  glTypeSize;
37   uint32_t  glFormat;  //(RGB, RGBA, BGRA, etc.)
38   uint32_t  glInternalFormat; //For uncompressed textures, specifies the internalformat parameter passed to glTexStorage*D or glTexImage*D
39   uint32_t  glBaseInternalFormat;
40   uint32_t  pixelWidth;
41   uint32_t  pixelHeight;
42   uint32_t  pixelDepth;
43   uint32_t  numberOfArrayElements;
44   uint32_t  numberOfFaces; //Cube map faces are stored in the order: +X, -X, +Y, -Y, +Z, -Z.
45   uint32_t  numberOfMipmapLevels;
46   uint32_t  bytesOfKeyValueData;
47 };
48
49 /**
50  * Convert KTX format to Dali::Pixel::Format
51  */
52 bool ConvertPixelFormat(const uint32_t ktxPixelFormat, Dali::Pixel::Format& format)
53 {
54   switch( ktxPixelFormat )
55   {
56     case 0x93B0: // GL_COMPRESSED_RGBA_ASTC_4x4_KHR
57     {
58       format = Dali::Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR;
59       break;
60     }
61     case 0x881B:// GL_RGB16F
62     {
63       format = Dali::Pixel::RGB16F;
64       break;
65     }
66     case 0x8815: // GL_RGB32F
67     {
68       format = Dali::Pixel::RGB32F;
69       break;
70     }
71     case 0x8C3A: // GL_R11F_G11F_B10F
72     {
73       format = Dali::Pixel::RGB32F;
74       break;
75     }
76     case 0x8D7C: // GL_RGBA8UI
77     {
78       format = Dali::Pixel::RGBA8888;
79       break;
80     }
81     case 0x8D7D: // GL_RGB8UI
82     {
83       format = Dali::Pixel::RGB888;
84       break;
85     }
86     default:
87     {
88       return false;
89     }
90   }
91
92   return true;
93 }
94
95 bool LoadCubeMapFromKtxFile( const std::string& path, CubeData& cubedata )
96 {
97   Dali::FileStream fileStream( path, Dali::FileStream::READ | Dali::FileStream::BINARY );
98   FILE* fp = fileStream.GetFile();
99   if( NULL == fp )
100   {
101     return false;
102   }
103
104   KtxFileHeader header;
105
106   int result = fread(&header,1,sizeof(KtxFileHeader),fp);
107   if( 0 == result )
108   {
109     return false;
110   }
111
112   long lSize = 0;
113
114   // Skip the key-values:
115   const long int imageSizeOffset = sizeof(KtxFileHeader) + header.bytesOfKeyValueData;
116
117   if( fseek(fp, imageSizeOffset, SEEK_END) )
118   {
119     return false;
120   }
121
122   lSize = ftell(fp);
123   if( lSize == -1L )
124   {
125     return false;
126   }
127
128   rewind(fp);
129
130   if( fseek(fp, imageSizeOffset, SEEK_SET) )
131   {
132     return false;
133   }
134
135   cubedata.img.resize(header.numberOfFaces);
136
137   for(unsigned int face=0; face < header.numberOfFaces; ++face) //array_element must be 0 or 1
138   {
139     cubedata.img[face].resize(header.numberOfMipmapLevels);
140   }
141
142   unsigned char* buffer= reinterpret_cast<unsigned char*>( malloc( lSize ) );
143
144   unsigned char* img[6];
145   unsigned int imgSize[6];
146   unsigned char* imgPointer = buffer;
147   result = fread(buffer,1,lSize,fp);
148
149   if( 0 == result )
150   {
151     free( buffer );
152     return false;
153   }
154
155   if( 0 == header.numberOfMipmapLevels )
156   {
157     header.numberOfMipmapLevels = 1u;
158   }
159
160   if( 0 == header.numberOfArrayElements )
161   {
162     header.numberOfArrayElements = 1u;
163   }
164
165   if( 0 == header.pixelDepth )
166   {
167     header.pixelDepth = 1u;
168   }
169
170   if( 0 == header.pixelHeight )
171   {
172     header.pixelHeight = 1u;
173   }
174
175   Dali::Pixel::Format daliformat = Pixel::RGB888;
176
177   ConvertPixelFormat(header.glInternalFormat, daliformat);
178
179   for( unsigned int mipmapLevel = 0; mipmapLevel < header.numberOfMipmapLevels; ++mipmapLevel )
180   {
181     long int byteSize = 0;
182     int imageSize;
183     memcpy(&imageSize,imgPointer,sizeof(unsigned int));
184     imgPointer += 4u;
185     for(unsigned int arrayElement=0; arrayElement < header.numberOfArrayElements; ++arrayElement) //arrayElement must be 0 or 1
186     {
187       for(unsigned int face=0; face < header.numberOfFaces; ++face)
188       {
189         byteSize = imageSize;
190         if(byteSize % 4u)
191         {
192           byteSize += 4u - byteSize % 4u;
193         }
194         img[face] = reinterpret_cast<unsigned char*>( malloc( byteSize ) ); // resources will be freed when the PixelData is destroyed.
195         memcpy(img[face],imgPointer,byteSize);
196         imgSize[face] = byteSize;
197         imgPointer += byteSize;
198         cubedata.img[face][mipmapLevel] = PixelData::New( img[face], imgSize[face], header.pixelWidth , header.pixelHeight , daliformat, PixelData::FREE );
199       }
200     }
201     header.pixelHeight/=2u;
202     header.pixelWidth/=2u;
203   }
204
205   free(buffer);
206   return true;
207 }
208
209 } // namespace PbrDemo