1// tImageKTX.cpp 
2// 
3// This knows how to load/save KTX files. It knows the details of the ktx and ktx2 file format and loads the data into 
4// multiple tPixel arrays, one for each frame (KTKs may be animated). These arrays may be 'stolen' by tPictures. 
5// 
6// Copyright (c) 2022-2024 Tristan Grimmer. 
7// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
8// granted, provided that the above copyright notice and this permission notice appear in all copies. 
9// 
10// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
11// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
12// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
13// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
14// PERFORMANCE OF THIS SOFTWARE. 
15 
16#include <Foundation/tString.h> 
17#include <Foundation/tSmallFloat.h> 
18#include <System/tMachine.h> 
19#include "Image/tImageKTX.h" 
20#include "Image/tPixelUtil.h" 
21#include "Image/tPicture.h" 
22#include "bcdec/bcdec.h" 
23#include "etcdec/etcdec.h" 
24#include "astcenc.h" 
25#define KHRONOS_STATIC 
26#include "LibKTX/include/ktx.h" 
27#include "LibKTX/include/vulkan_core.h" 
28#include "LibKTX/include/gl_format.h" 
29namespace tImage 
30
31 
32 
33// Helper functions, enums, and types for parsing KTX files. 
34namespace tKTX 
35
36 // These figure out the pixel-format, colour-profile, alpha-mode, and channe-type. tPixelFormat does not specify 
37 // ancilllary properties of the data -- it specified the encoding of the data. The extra information, like the 
38 // colour-profile itcwas authored in, is stored in tColourProfile, tAlphaMode, and tChannelType. In many cases this 
39 // satellite information cannot be determined, in which case they get set the their 'unspecified' enumerants. 
40 void GetFormatInfo_FromGLFormat(tPixelFormat&, tColourProfile&, tAlphaMode&, tChannelType&, uint32 glType, uint32 glFormat, uint32 glInternalFormat); 
41 void GetFormatInfo_FromVKFormat(tPixelFormat&, tColourProfile&, tAlphaMode&, tChannelType&, uint32 vkFormat); 
42
43 
44 
45void tKTX::GetFormatInfo_FromGLFormat(tPixelFormat& format, tColourProfile& profile, tAlphaMode& alphaMode, tChannelType& chanType, uint32 glType, uint32 glFormat, uint32 glInternalFormat
46
47 // For colour profile (the space of the data) we try to make an educated guess. In general only the asset author 
48 // knows the colour space/profile. For most (non-HDR) pixel formats for colours, we assume the data is sRGB. 
49 // Floating-point formats are likewise assumed to be in linear-space (and are usually used for HDR images). In 
50 // addition when the data is probably not colour data (like ATI1/2) we assume it's in linear. 
51 format = tPixelFormat::Invalid
52 profile = tColourProfile::sRGB
53 alphaMode = tAlphaMode::None
54 chanType = tChannelType::NONE
55 
56 // First deal with compressed formats. For these the internal-format must be specified and can be used to determine 
57 // the format of the data in the ktx file. See https://registry.khronos.org/KTX/specs/1.0/ktxspec_v1.html 
58 // In cases where it's not a compressed format, the internal-format should not be queried to determine the format 
59 // of the ktx file data because it represents the desired format the data should be converted to when binding -- 
60 // all we care about is the format of the actual data, and glType/glFormat can be used to determine that. 
61 // Exception: The internal format is used to determine some non-compressed formats. 
62 // 
63 // Regardiing colour profiles. In many cases there are two similar GL pixel formats that differ only in that one of 
64 // them specifies using sRGB for the RGB channels. For example: 
65 // SRGB_ALPHA_S3TC_DXT3_EXT : sRGB for RGB, linear for A. 
66 // RGBA_S3TC_DXT3_EXT : No-sRGB variant. 
67 // The implication is that RGBA_S3TC_DXT3_EXT should therefore be in linear RGBA space. Unfortunately most files in 
68 // the wild that have these non-sRGB variants are usually still authored in sRGB-space. Tacent keeps them by default 
69 // in sRGB-space but you will see a commented-out lRGB tag in the switch below (indicating how it 'should' be). 
70 #define C(c) case GL_##c 
71 #define CC(c) case GL_COMPRESSED_##c 
72 #define F(f) format = tPixelFormat::f; 
73 #define P(p) profile = tColourProfile::p; 
74 #define M(m) alphaMode = tAlphaMode::m; 
75 #define T(t) chanType = tChannelType::t; 
76 switch (glInternalFormat
77
78 // 
79 // BC formats. 
80 // 
81 CC(RGB_S3TC_DXT1_EXT): F(BC1DXT1) /*P(lRGB)*/ M(None) T(NONE) break
82 CC(SRGB_S3TC_DXT1_EXT): F(BC1DXT1) break
83 CC(RGBA_S3TC_DXT1_EXT): F(BC1DXT1A) /*P(lRGB)*/ break
84 CC(SRGB_ALPHA_S3TC_DXT1_EXT): F(BC1DXT1A) break
85 CC(RGBA_S3TC_DXT3_EXT): F(BC2DXT2DXT3) /*P(lRGB)*/ break
86 CC(SRGB_ALPHA_S3TC_DXT3_EXT): F(BC2DXT2DXT3) break
87 CC(RGBA_S3TC_DXT5_EXT): F(BC3DXT4DXT5) /*P(lRGB)*/ break
88 CC(SRGB_ALPHA_S3TC_DXT5_EXT): F(BC3DXT4DXT5) break
89 CC(RED_RGTC1): F(BC4ATI1U) P(lRGB) T(UNORM) break
90 CC(SIGNED_RED_RGTC1): F(BC4ATI1S) P(lRGB) T(SNORM) break
91 CC(RG_RGTC2): F(BC5ATI2U) P(lRGB) T(UNORM) break
92 CC(SIGNED_RG_RGTC2): F(BC5ATI2S) P(lRGB) T(SNORM) break
93 CC(RGB_BPTC_UNSIGNED_FLOAT): F(BC6U) P(HDRa) T(UFLOAT) break
94 CC(RGB_BPTC_SIGNED_FLOAT): F(BC6S) P(HDRa) T(SFLOAT) break
95 
96 // BPTC AKA BC7 is designed for UNORM data. 
97 CC(RGBA_BPTC_UNORM): F(BC7) /*P(lRGB)*/ T(UNORM) break
98 CC(SRGB_ALPHA_BPTC_UNORM): F(BC7) T(UNORM) break
99 
100 // 
101 // ETC and EAC formats. 
102 // 
103 C(ETC1_RGB8_OES): F(ETC1) break
104 CC(RGB8_ETC2): F(ETC2RGB) /*P(lRGB)*/ break
105 CC(SRGB8_ETC2): F(ETC2RGB) break
106 CC(RGBA8_ETC2_EAC): F(ETC2RGBA) /*P(lRGB)*/ break
107 CC(SRGB8_ALPHA8_ETC2_EAC): F(ETC2RGBA) break
108 CC(RGB8_PUNCHTHROUGH_ALPHA1_ETC2): F(ETC2RGBA1) /*P(lRGB)*/ break
109 CC(SRGB8_PUNCHTHROUGH_ALPHA1_ETC2): F(ETC2RGBA1) break
110 
111 // Leaving the R and RG formats in sRGB space. 
112 CC(R11_EAC): F(EACR11U) T(UINT) break
113 CC(SIGNED_R11_EAC): F(EACR11S) T(SINT) break
114 CC(RG11_EAC): F(EACRG11U) T(UINT) break
115 CC(SIGNED_RG11_EAC): F(EACRG11S) T(SINT) break
116 
117 // 
118 // PVR formats. 
119 // 
120 // These are a bit badly named by OpenGL as there is no distinction between 3 and 4 component overall. It is a per-block 
121 // quantity that determines if it has alpha or not. 
122 CC(RGBA_PVRTC_4BPPV1_IMG): F(PVRBPP4) P(lRGB) T(UNORM) break; // 4-component PVRTC, 8x8 blocks, unsigned normalized. 
123 CC(SRGB_ALPHA_PVRTC_4BPPV1_EXT): F(PVRBPP4) P(sRGB) T(UINT) break; // 4-component PVRTC, 8x8 blocks, sRGB. 
124 CC(RGB_PVRTC_4BPPV1_IMG): F(PVRBPP4) P(lRGB) T(UNORM) break; // 3-component PVRTC, 8x8 blocks, unsigned normalized. 
125 CC(SRGB_PVRTC_4BPPV1_EXT): F(PVRBPP4) P(sRGB) T(UINT) break; // 3-component PVRTC, 8x8 blocks, sRGB. 
126 CC(RGBA_PVRTC_2BPPV1_IMG): F(PVRBPP2) P(lRGB) T(UNORM) break; // 4-component PVRTC, 16x8 blocks, unsigned normalized. 
127 CC(SRGB_ALPHA_PVRTC_2BPPV1_EXT): F(PVRBPP2) P(sRGB) T(UINT) break; // 4-component PVRTC, 16x8 blocks, sRGB. 
128 CC(RGB_PVRTC_2BPPV1_IMG): F(PVRBPP2) P(lRGB) T(UNORM) break; // 3-component PVRTC, 16x8 blocks, unsigned normalized. 
129 CC(SRGB_PVRTC_2BPPV1_EXT): F(PVRBPP2) P(sRGB) T(UINT) break; // 3-component PVRTC, 16x8 blocks, sRGB. 
130 //CC(RGBA_PVRTC_2BPPV2_IMG): 
131 //CC(SRGB_ALPHA_PVRTC_2BPPV2_IMG): 
132 //CC(RGBA_PVRTC_4BPPV2_IMG): 
133 //CC(SRGB_ALPHA_PVRTC_4BPPV2_IMG): 
134 
135 // 
136 // For ASTC formats we assume HDR-linear space if SRGB not specified. 
137 // 
138 // We chose HDR as the default profile because it can load LDR blocks. The other way around doesn't work with 
139 // with the test images -- the LDR profile doesn't appear capable of loading HDR blocks (they become magenta). 
140 // 
141 CC(RGBA_ASTC_4x4_KHR): F(ASTC4X4) P(HDRa) break
142 CC(SRGB8_ALPHA8_ASTC_4x4_KHR): F(ASTC4X4) break
143 CC(RGBA_ASTC_5x4_KHR): F(ASTC5X4) P(HDRa) break
144 CC(SRGB8_ALPHA8_ASTC_5x4_KHR): F(ASTC5X4) break
145 CC(RGBA_ASTC_5x5_KHR): F(ASTC5X5) P(HDRa) break
146 CC(SRGB8_ALPHA8_ASTC_5x5_KHR): F(ASTC5X5) break
147 CC(RGBA_ASTC_6x5_KHR): F(ASTC6X5) P(HDRa) break
148 CC(SRGB8_ALPHA8_ASTC_6x5_KHR): F(ASTC6X5) break
149 CC(RGBA_ASTC_6x6_KHR): F(ASTC6X6) P(HDRa) break
150 CC(SRGB8_ALPHA8_ASTC_6x6_KHR): F(ASTC6X6) break
151 CC(RGBA_ASTC_8x5_KHR): F(ASTC8X5) P(HDRa) break
152 CC(SRGB8_ALPHA8_ASTC_8x5_KHR): F(ASTC8X5) break
153 CC(RGBA_ASTC_8x6_KHR): F(ASTC8X6) P(HDRa) break
154 CC(SRGB8_ALPHA8_ASTC_8x6_KHR): F(ASTC8X6) break
155 CC(RGBA_ASTC_8x8_KHR): F(ASTC8X8) P(HDRa) break
156 CC(SRGB8_ALPHA8_ASTC_8x8_KHR): F(ASTC8X8) break
157 CC(RGBA_ASTC_10x5_KHR): F(ASTC10X5) P(HDRa) break
158 CC(SRGB8_ALPHA8_ASTC_10x5_KHR): F(ASTC10X5) break
159 CC(RGBA_ASTC_10x6_KHR): F(ASTC10X6) P(HDRa) break
160 CC(SRGB8_ALPHA8_ASTC_10x6_KHR): F(ASTC10X6) break
161 CC(RGBA_ASTC_10x8_KHR): F(ASTC10X8) P(HDRa) break
162 CC(SRGB8_ALPHA8_ASTC_10x8_KHR): F(ASTC10X8) break
163 CC(RGBA_ASTC_10x10_KHR): F(ASTC10X10) P(HDRa) break
164 CC(SRGB8_ALPHA8_ASTC_10x10_KHR): F(ASTC10X10) break
165 CC(RGBA_ASTC_12x10_KHR): F(ASTC12X10) P(HDRa) break
166 CC(SRGB8_ALPHA8_ASTC_12x10_KHR): F(ASTC12X10) break
167 CC(RGBA_ASTC_12x12_KHR): F(ASTC12X12) P(HDRa) break
168 CC(SRGB8_ALPHA8_ASTC_12x12_KHR): F(ASTC12X12) break
169 
170 // Decided to specify the channel-type as full floats rather than half. Ref implementations of 
171 // B10G11R11uf and E5B9G9R9uf all convert to float rather than half. They are both unsigned. 
172 C(R11F_G11F_B10F): F(B10G11R11uf) P(HDRa) T(UFLOAT) break
173 C(RGB9_E5): F(E5B9G9R9uf) P(HDRa) T(UFLOAT) break
174 C(RGBA4): F(B4A4R4G4) P(lRGB) T(UINT) break
175
176 
177 if (format != tPixelFormat::Invalid
178 return
179 
180 // Not a compressed format. Look at glFormat. 
181 switch (glFormat
182
183 C(LUMINANCE): 
184 switch (glType
185
186 C(UNSIGNED_BYTE): F(L8) T(UINT) break
187
188 break
189 
190 C(ALPHA): 
191 switch (glType
192
193 C(UNSIGNED_BYTE): F(A8) P(lRGB) T(UINT) break
194
195 break
196 
197 // It's ok to also include the 'INTEGER' versions here as it just restricts what the glType 
198 // may be. For example R32f would still be GL_RED with type GL_FLOAT, and R8 could be either 
199 // GL_RED/GL_UNSIGNED_BYTE or GL_RED_INTEGER/GL_UNSIGNED_BYTE. 
200 C(RED): 
201 C(RED_INTEGER): 
202 switch (glType
203
204 C(UNSIGNED_BYTE): F(R8) T(UINT) break
205 C(HALF_FLOAT): F(R16f) P(HDRa) T(SFLOAT) break
206 C(FLOAT): F(R32f) P(HDRa) T(SFLOAT) break
207
208 break
209  
210 C(RG): 
211 C(RG_INTEGER): 
212 switch (glType
213
214 C(UNSIGNED_BYTE): F(R8G8) T(UINT) break
215 C(HALF_FLOAT): F(R16G16f) P(HDRa) T(SFLOAT) break
216 C(FLOAT): F(R32G32f) P(HDRa) T(SFLOAT) break
217
218 break
219 
220 C(RGB): 
221 C(RGB_INTEGER): 
222 switch (glType
223
224 // They are all T(UINT8). The short refers to total pixel size but each component would still 
225 // be accessed as a uint8 in a shader program. GL is a bit inconsistent here as the BYTE type 
226 // does not refer to the total pixel size like SHORT does. 
227 C(UNSIGNED_BYTE): F(R8G8B8) T(UINT) break
228 C(UNSIGNED_SHORT_5_6_5_REV): F(G3B5R5G3) T(UINT) break
229 C(HALF_FLOAT): F(R16G16B16f) P(HDRa) T(SFLOAT) break
230 C(FLOAT): F(R32G32B32f) P(HDRa) T(SFLOAT) break
231
232 break
233 
234 C(RGBA): 
235 C(RGBA_INTEGER): 
236 switch (glType
237
238 C(UNSIGNED_SHORT_4_4_4_4): F(B4A4R4G4) T(UINT) break
239 C(UNSIGNED_BYTE): F(R8G8B8A8) T(UINT) break
240 C(HALF_FLOAT): F(R16G16B16A16f) P(HDRa) T(SFLOAT) break
241 C(FLOAT): F(R32G32B32A32f) P(HDRa) T(SFLOAT) break
242
243 break
244 
245 C(BGR): 
246 C(BGR_INTEGER): 
247 switch (glType
248
249 // See comment above. 
250 C(UNSIGNED_BYTE): F(B8G8R8) T(UINT) break
251 C(UNSIGNED_SHORT_5_6_5): F(G3B5R5G3) T(UINT) break
252
253 break
254 
255 C(BGRA): 
256 C(BGRA_INTEGER): 
257 switch (glType
258
259 // See comment above. 
260 C(UNSIGNED_BYTE): F(B8G8R8A8) T(UINT) break
261 C(UNSIGNED_SHORT_4_4_4_4): F(G4B4A4R4) T(UINT) break
262 C(UNSIGNED_SHORT_5_5_5_1): F(G3B5A1R5G2) T(UINT) break
263
264 break
265
266 
267 if (format == tPixelFormat::Invalid
268 P(Unspecified
269 
270 #undef C 
271 #undef CC 
272 #undef F 
273 #undef P 
274 #undef M 
275 #undef T 
276
277 
278 
279void tKTX::GetFormatInfo_FromVKFormat(tPixelFormat& format, tColourProfile& profile, tAlphaMode& alphaMode, tChannelType& chanType, uint32 vkFormat
280
281 // For colour profile (the space of the data) we try to make an educated guess. In general only the asset author 
282 // knows the colour space/profile. For most (non-HDR) pixel formats for colours, we assume the data is sRGB. 
283 // Floating-point formats are likewise assumed to be in linear-space (and are usually used for HDR images). In 
284 // addition when the data is probably not colour data (like ATI1/2) we assume it's in linear. 
285 format = tPixelFormat::Invalid
286 profile = tColourProfile::sRGB
287 alphaMode = tAlphaMode::None
288 chanType = tChannelType::NONE
289 
290 // The VK formats conflate the format with the data. The colour-space is not part of the format in tacent and is 
291 // returned in a separate variable. UNORM means E [0.0, 1.0]. 
292 // 
293 // Regardiing colour profiles. In many cases there are two similar VK pixel formats that differ only in that one of 
294 // them specifies using sRGB for the RGB channels. For example: 
295 // VK_FORMAT_R8G8B8A8_SRGB : sRGB for RGB, linear for A. 
296 // VK_FORMAT_R8G8B8A8_UNORM : No-sRGB variant. 
297 // The implication is that VK_FORMAT_R8G8B8A8_UNORM should therefore be in linear RGBA space. Unfortunately most 
298 // files in the wild that have these non-sRGB variants are usually still authored in sRGB-space. Tacent keeps them 
299 // by default in sRGB-space but you will see a commented-out lRGB tag below (indicating how it 'should' be). 
300 #define C(c) case VK_FORMAT_##c 
301 #define F(f) format = tPixelFormat::f; 
302 #define P(p) profile = tColourProfile::p; 
303 #define M(m) alphaMode = tAlphaMode::m; 
304 #define T(t) chanType = tChannelType::t; 
305 switch (vkFormat
306
307 // 
308 // Packed formats. 
309 // 
310 // NVTT can export ktx2 as A8. There is no A8 in VkFormat so it uses R8 instead. 
311 // There is no difference in storage between UNORM (unsigned normalized) and UINT. The only difference is 
312 // when the texture is bound, the UNORM textures get their component values converted to floats in [0.0, 1.0], 
313 // whereas the UINT textures would just have the int returned by a shader texture sampler. Commented-out 
314 // case statements are simply not implemented yet. 
315 // 
316 //C(R8_SNORM): 
317 //C(R8_USCALED): 
318 //C(R8_SSCALED): 
319 //C(R8_SINT): 
320 C(R8_UNORM): F(R8) /*P(lRGB)*/ M(None) T(UNORM) break
321 C(R8_UINT): F(R8) /*P(lRGB)*/ T(UINT) break
322 C(R8_SRGB): F(R8) break
323 
324 //C(R8G8_SNORM): 
325 //C(R8G8_USCALED): 
326 //C(R8G8_SSCALED): 
327 //C(R8G8_SINT): 
328 C(R8G8_UNORM): F(R8G8) /*P(lRGB)*/ T(UNORM) break
329 C(R8G8_UINT): F(R8G8) /*P(lRGB)*/ T(UINT) break
330 C(R8G8_SRGB): F(R8G8) break
331 
332 //C(R8G8B8_SNORM): 
333 //C(R8G8B8_USCALED): 
334 //C(R8G8B8_SSCALED): 
335 //C(R8G8B8_SINT): 
336 C(R8G8B8_UNORM): F(R8G8B8) /*P(lRGB)*/ T(UNORM) break
337 C(R8G8B8_UINT): F(R8G8B8) /*P(lRGB)*/ T(UINT) break
338 C(R8G8B8_SRGB): F(R8G8B8) break
339  
340 //C(R8G8B8A8_SNORM): 
341 //C(R8G8B8A8_USCALED): 
342 //C(R8G8B8A8_SSCALED): 
343 //C(R8G8B8A8_SINT): 
344 C(R8G8B8A8_UNORM): F(R8G8B8A8) /*P(lRGB)*/ T(UNORM) break
345 C(R8G8B8A8_UINT): F(R8G8B8A8) /*P(lRGB)*/ T(UINT) break
346 C(R8G8B8A8_SRGB): F(R8G8B8A8) break
347 
348 //C(B8G8R8_SNORM): 
349 //C(B8G8R8_USCALED): 
350 //C(B8G8R8_SSCALED): 
351 //C(B8G8R8_SINT): 
352 C(B8G8R8_UNORM): F(B8G8R8) /*P(lRGB)*/ T(UNORM) break
353 C(B8G8R8_UINT): F(B8G8R8) /*P(lRGB)*/ T(UINT) break
354 C(B8G8R8_SRGB): F(B8G8R8) break
355 
356 //C(B8G8R8A8_SNORM): 
357 //C(B8G8R8A8_USCALED): 
358 //C(B8G8R8A8_SSCALED): 
359 //C(B8G8R8A8_SINT): 
360 C(B8G8R8A8_UNORM): F(B8G8R8A8) /*P(lRGB)*/ T(UNORM) break
361 C(B8G8R8A8_UINT): F(B8G8R8A8) /*P(lRGB)*/ T(UINT) break
362 C(B8G8R8A8_SRGB): F(B8G8R8A8) break
363 
364 C(B5G6R5_UNORM_PACK16): F(G3B5R5G3) T(UNORM) break
365 C(B4G4R4A4_UNORM_PACK16): F(G4B4A4R4) T(UNORM) break
366 C(B5G5R5A1_UNORM_PACK16): F(G3B5A1R5G2) T(UNORM) break
367 
368 C(R16_SFLOAT): F(R16f) P(HDRa) T(SFLOAT) break
369 C(R16G16_SFLOAT): F(R16G16f) P(HDRa) T(SFLOAT) break
370 C(R16G16B16_SFLOAT): F(R16G16B16f) P(HDRa) T(SFLOAT) break
371 C(R16G16B16A16_SFLOAT): F(R16G16B16A16f) P(HDRa) T(SFLOAT) break
372 C(R32_SFLOAT): F(R32f) P(HDRa) T(SFLOAT) break
373 C(R32G32_SFLOAT): F(R32G32f) P(HDRa) T(SFLOAT) break
374 C(R32G32B32_SFLOAT): F(R32G32B32f) P(HDRa) T(SFLOAT) break
375 C(R32G32B32A32_SFLOAT): F(R32G32B32A32f) P(HDRa) T(SFLOAT) break
376 C(B10G11R11_UFLOAT_PACK32): F(B10G11R11uf) P(HDRa) T(UFLOAT) break;  
377 C(E5B9G9R9_UFLOAT_PACK32): F(E5B9G9R9uf) P(HDRa) T(UFLOAT) break
378 
379 // 
380 // BC Formats. 
381 // 
382 C(BC1_RGB_UNORM_BLOCK): F(BC1DXT1) /*P(lRGB)*/ T(UNORM) break
383 C(BC1_RGB_SRGB_BLOCK): F(BC1DXT1) break
384 C(BC1_RGBA_UNORM_BLOCK): F(BC1DXT1A) /*P(lRGB)*/ T(UNORM) break
385 C(BC1_RGBA_SRGB_BLOCK): F(BC1DXT1A) break
386 C(BC2_UNORM_BLOCK): F(BC2DXT2DXT3) /*P(lRGB)*/ T(UNORM) break
387 C(BC2_SRGB_BLOCK): F(BC2DXT2DXT3) break
388 C(BC3_UNORM_BLOCK): F(BC3DXT4DXT5) /*P(lRGB)*/ T(UNORM) break
389 C(BC3_SRGB_BLOCK): F(BC3DXT4DXT5) break
390 
391 // Signed not supported yet for BC4 and BC5. 
392 C(BC4_UNORM_BLOCK): F(BC4ATI1U) /*P(lRGB)*/ T(UNORM) break
393 C(BC4_SNORM_BLOCK): F(BC4ATI1S) /*P(lRGB)*/ T(SNORM) break
394 C(BC5_UNORM_BLOCK): F(BC5ATI2U) /*P(lRGB)*/ T(UNORM) break
395 C(BC5_SNORM_BLOCK): F(BC5ATI2S) /*P(lRGB)*/ T(SNORM) break
396 
397 C(BC6H_UFLOAT_BLOCK): F(BC6U) P(HDRa) T(UFLOAT) break
398 C(BC6H_SFLOAT_BLOCK): F(BC6S) P(HDRa) T(SFLOAT) break
399 
400 C(BC7_UNORM_BLOCK): F(BC7) /*P(lRGB)*/ T(UNORM) break
401 C(BC7_SRGB_BLOCK): F(BC7) break
402 
403 // 
404 // ETC2 and EAC Formats. 
405 // 
406 C(ETC2_R8G8B8_UNORM_BLOCK): F(ETC2RGB) /*P(lRGB)*/ T(UNORM) break
407 C(ETC2_R8G8B8_SRGB_BLOCK): F(ETC2RGB) break
408 C(ETC2_R8G8B8A8_UNORM_BLOCK): F(ETC2RGBA) /*P(lRGB)*/ T(UNORM) break
409 C(ETC2_R8G8B8A8_SRGB_BLOCK): F(ETC2RGBA) break
410 C(ETC2_R8G8B8A1_UNORM_BLOCK): F(ETC2RGBA1) /*P(lRGB)*/ T(UNORM) break
411 C(ETC2_R8G8B8A1_SRGB_BLOCK): F(ETC2RGBA1) break
412 
413 C(EAC_R11_UNORM_BLOCK): F(EACR11U) T(UNORM) break
414 C(EAC_R11_SNORM_BLOCK): F(EACR11S) T(SFLOAT) break
415 C(EAC_R11G11_UNORM_BLOCK): F(EACRG11U) T(UNORM) break
416 C(EAC_R11G11_SNORM_BLOCK): F(EACRG11S) T(SFLOAT) break
417 
418 // 
419 // PVR Formats. 
420 // 
421 C(PVRTC1_4BPP_UNORM_BLOCK_IMG): F(PVRBPP4) P(lRGB) T(UNORM) break
422 C(PVRTC1_4BPP_SRGB_BLOCK_IMG): F(PVRBPP4) P(sRGB) T(UINT) break
423 C(PVRTC1_2BPP_UNORM_BLOCK_IMG): F(PVRBPP2) P(lRGB) T(UNORM) break
424 C(PVRTC1_2BPP_SRGB_BLOCK_IMG): F(PVRBPP2) P(sRGB) T(UINT) break
425 //C(PVRTC2_4BPP_UNORM_BLOCK_IMG): 
426 //C(PVRTC2_4BPP_SRGB_BLOCK_IMG): 
427 //C(PVRTC2_2BPP_UNORM_BLOCK_IMG): 
428 //C(PVRTC2_2BPP_SRGB_BLOCK_IMG): 
429 
430 // 
431 // ASTC 
432 // 
433 // From the Khronos description of ASTC:  
434 // "Whether floats larger than 1.0 are allowed is not a per-image property; it's a per-block property. An 
435 // HDR-compressed ASTC image is simply one where blocks can return values larger than 1.0." and "So the format 
436 // does not specify if floating point values are greater than 1.0." and "There are only two properties of an 
437 // ASTC compressed image that are per-image (and therefore part of the format) rather than being per-block. 
438 // These properties are block size and sRGB colorspace conversion." 
439 // 
440 // It seems to me for an HDR ASTC KTX2 image there are two possibilities for VK_FORMAT: 
441 //  
442 // 1) VK_FORMAT_ASTC_4x4_UNORM_BLOCK or VK_FORMAT_ASTC_4x4_SRGB_BLOCK, in which case blocks that return 
443 // component values > 1.0 are making a liar out of "UNORM" -- and 
444 // 2) VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT 
445 // 
446 // Which one is correct? AMD's compressonator, after converting an EXR to ASTC, it can't guarantee blocks won't 
447 // return values above 1.0 (i.e. an HDR image). It does not generate SFLOAT_BLOCK_EXT. I suspect compressionator 
448 // is incorrect. 
449 // 
450 // We chose HDR as the default profile because it can load LDR blocks. The other way around doesn't work with 
451 // with the tests images -- the LDR profile doesn't appear capable of loading HDR blocks (they become magenta). 
452 // 
453 C(ASTC_4x4_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
454 C(ASTC_4x4_UNORM_BLOCK): F(ASTC4X4) P(HDRa) T(UNORM) break
455 C(ASTC_4x4_SRGB_BLOCK): F(ASTC4X4) break
456 
457 C(ASTC_5x4_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
458 C(ASTC_5x4_UNORM_BLOCK): F(ASTC5X4) P(HDRa) T(UNORM) break
459 C(ASTC_5x4_SRGB_BLOCK): F(ASTC5X4) break
460 
461 C(ASTC_5x5_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
462 C(ASTC_5x5_UNORM_BLOCK): F(ASTC5X5) P(HDRa) T(UNORM) break
463 C(ASTC_5x5_SRGB_BLOCK): F(ASTC5X5) break
464 
465 C(ASTC_6x5_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
466 C(ASTC_6x5_UNORM_BLOCK): F(ASTC6X5) P(HDRa) T(UNORM) break
467 C(ASTC_6x5_SRGB_BLOCK): F(ASTC6X5) break
468 
469 C(ASTC_6x6_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
470 C(ASTC_6x6_UNORM_BLOCK): F(ASTC6X6) P(HDRa) T(UNORM) break
471 C(ASTC_6x6_SRGB_BLOCK): F(ASTC6X6) break
472 
473 C(ASTC_8x5_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
474 C(ASTC_8x5_UNORM_BLOCK): F(ASTC8X5) P(HDRa) T(UNORM) break
475 C(ASTC_8x5_SRGB_BLOCK): F(ASTC8X5) break
476 
477 C(ASTC_8x6_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
478 C(ASTC_8x6_UNORM_BLOCK): F(ASTC8X6) P(HDRa) T(UNORM) break
479 C(ASTC_8x6_SRGB_BLOCK): F(ASTC8X6) break
480 
481 C(ASTC_8x8_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
482 C(ASTC_8x8_UNORM_BLOCK): F(ASTC8X8) P(HDRa) T(UNORM) break
483 C(ASTC_8x8_SRGB_BLOCK): F(ASTC8X8) break
484 
485 C(ASTC_10x5_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
486 C(ASTC_10x5_UNORM_BLOCK): F(ASTC10X5) P(HDRa) T(UNORM) break
487 C(ASTC_10x5_SRGB_BLOCK): F(ASTC10X5) break
488 
489 C(ASTC_10x6_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
490 C(ASTC_10x6_UNORM_BLOCK): F(ASTC10X6) P(HDRa) T(UNORM) break
491 C(ASTC_10x6_SRGB_BLOCK): F(ASTC10X6) break
492 
493 C(ASTC_10x8_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
494 C(ASTC_10x8_UNORM_BLOCK): F(ASTC10X8) P(HDRa) T(UNORM) break
495 C(ASTC_10x8_SRGB_BLOCK): F(ASTC10X8) break
496 
497 C(ASTC_10x10_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
498 C(ASTC_10x10_UNORM_BLOCK): F(ASTC10X10) P(HDRa) T(UNORM) break
499 C(ASTC_10x10_SRGB_BLOCK): F(ASTC10X10) break
500 
501 C(ASTC_12x10_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
502 C(ASTC_12x10_UNORM_BLOCK): F(ASTC12X10) P(HDRa) T(UNORM) break
503 C(ASTC_12x10_SRGB_BLOCK): F(ASTC12X10) break
504 
505 C(ASTC_12x12_SFLOAT_BLOCK_EXT): F(ASTC4X4) P(HDRa) T(SFLOAT) break
506 C(ASTC_12x12_UNORM_BLOCK): F(ASTC12X12) P(HDRa) T(UNORM) break
507 C(ASTC_12x12_SRGB_BLOCK): F(ASTC12X12) break
508
509 
510 if (format == tPixelFormat::Invalid
511 P(Unspecified); 
512 
513 #undef C 
514 #undef F 
515 #undef P 
516 #undef M 
517 #undef T 
518
519 
520 
521tImageKTX::tImageKTX() 
522
523 tStd::tMemset(dest: Layers, val: 0, numBytes: sizeof(Layers)); 
524
525 
526 
527tImageKTX::tImageKTX(const tString& ktxFile, const LoadParams& loadParams) : 
528 Filename(ktxFile
529
530 tStd::tMemset(dest: Layers, val: 0, numBytes: sizeof(Layers)); 
531 Load(ktxFile, loadParams); 
532
533 
534 
535tImageKTX::tImageKTX(const uint8* ktxFileInMemory, int numBytes, const LoadParams& loadParams
536
537 tStd::tMemset(dest: Layers, val: 0, numBytes: sizeof(Layers)); 
538 Load(ktxFileInMemory, numBytes, loadParams); 
539
540 
541 
542void tImageKTX::Clear() 
543
544 for (int image = 0; image < NumImages; image++) 
545
546 for (int layer = 0; layer < NumMipmapLayers; layer++) 
547
548 delete Layers[layer][image]; 
549 Layers[layer][image] = nullptr
550
551
552 
553 States = 0; // Image will be invalid now since Valid state not set. 
554 AlphaMode = tAlphaMode::Unspecified
555 ChannelType = tChannelType::Unspecified
556 IsCubeMap = false
557 RowReversalOperationPerformed = false
558 NumImages = 0
559 NumMipmapLayers = 0
560 
561 tBaseImage::Clear(); 
562
563 
564 
565bool tImageKTX::Set(tPixel4b* pixels, int width, int height, bool steal
566
567 Clear(); 
568 if (!pixels || (width <= 0) || (height <= 0)) 
569 return false
570 
571 Layers[0][0] = new tLayer(tPixelFormat::R8G8B8A8, width, height, (uint8*)pixels, steal); 
572 AlphaMode = tAlphaMode::Normal
573 ChannelType = tChannelType::UNORM
574 NumImages = 1
575 NumMipmapLayers = 1
576 
577 PixelFormatSrc = tPixelFormat::R8G8B8A8
578 PixelFormat = tPixelFormat::R8G8B8A8
579 ColourProfileSrc = tColourProfile::sRGB; // We assume pixels must be sRGB. 
580 ColourProfile = tColourProfile::sRGB
581 
582 SetStateBit(StateBit::Valid); 
583 return true
584
585 
586 
587bool tImageKTX::Set(tFrame* frame, bool steal
588
589 Clear(); 
590 if (!frame || !frame->IsValid()) 
591 return false
592 
593 PixelFormatSrc = frame->PixelFormatSrc
594 PixelFormat = tPixelFormat::R8G8B8A8
595 ColourProfileSrc = tColourProfile::sRGB; // We assume frame must be sRGB. 
596 ColourProfile = tColourProfile::sRGB
597 
598 tPixel4b* pixels = frame->GetPixels(steal); 
599 Set(pixels, width: frame->Width, height: frame->Height, steal); 
600 if (steal
601 delete frame
602 
603 SetStateBit(StateBit::Valid); 
604 return true
605
606 
607 
608bool tImageKTX::Set(tPicture& picture, bool steal
609
610 Clear(); 
611 if (!picture.IsValid()) 
612 return false
613 
614 PixelFormatSrc = picture.PixelFormatSrc
615 PixelFormat = tPixelFormat::R8G8B8A8
616 // We don't know colour profile of tPicture. 
617 
618 // This is worth some explanation. If steal is true the picture becomes invalid and the 
619 // 'set' call will steal the stolen pixels. If steal is false GetPixels is called and the 
620 // 'set' call will memcpy them out... which makes sure the picture is still valid after and 
621 // no-one is sharing the pixel buffer. We don't check the success of 'set' because it must 
622 // succeed if picture was valid. 
623 tPixel4b* pixels = steal ? picture.StealPixels() : picture.GetPixels(); 
624 bool success = Set(pixels, width: picture.GetWidth(), height: picture.GetHeight(), steal); 
625 tAssert(success); 
626 return true
627
628 
629 
630tFrame* tImageKTX::GetFrame(bool steal
631
632 // Data must be decoded for this to work. 
633 if (!IsValid() || (PixelFormat != tPixelFormat::R8G8B8A8) || (Layers[0][0] == nullptr)) 
634 return nullptr
635 
636 tFrame* frame = new tFrame(); 
637 frame->Width = Layers[0][0]->Width
638 frame->Height = Layers[0][0]->Height
639 frame->PixelFormatSrc = PixelFormatSrc
640 
641 if (steal
642
643 frame->Pixels = (tPixel4b*)Layers[0][0]->StealData(); 
644 delete Layers[0][0]; 
645 Layers[0][0] = nullptr
646
647 else 
648
649 frame->Pixels = new tPixel4b[frame->Width * frame->Height]; 
650 tStd::tMemcpy(dest: frame->Pixels, src: (tPixel4b*)Layers[0][0]->Data, numBytes: frame->Width * frame->Height * sizeof(tPixel4b)); 
651
652 
653 return frame
654
655 
656 
657bool tImageKTX::IsOpaque() const 
658
659 return tImage::tIsOpaqueFormat(format: PixelFormat); 
660
661 
662 
663bool tImageKTX::StealLayers(tList<tLayer>& layers
664
665 if (!IsValid() || IsCubemap() || (NumImages <= 0)) 
666 return false
667 
668 for (int mip = 0; mip < NumMipmapLayers; mip++) 
669
670 layers.Append(item: Layers[mip][0]); 
671 Layers[mip][0] = nullptr
672
673 
674 Clear(); 
675 return true
676
677 
678 
679int tImageKTX::GetLayers(tList<tLayer>& layers) const 
680
681 if (!IsValid() || IsCubemap() || (NumImages <= 0)) 
682 return 0
683 
684 for (int mip = 0; mip < NumMipmapLayers; mip++) 
685 layers.Append(item: Layers[mip][0]); 
686 
687 return NumMipmapLayers
688
689 
690 
691int tImageKTX::StealCubemapLayers(tList<tLayer> layerLists[tFaceIndex_NumFaces], uint32 faceFlags
692
693 if (!IsValid() || !IsCubemap() || !faceFlags
694 return 0
695 
696 int faceCount = 0
697 for (int face = 0; face < tFaceIndex_NumFaces; face++) 
698
699 uint32 faceFlag = 1 << face
700 if (!(faceFlag & faceFlags)) 
701 continue
702 
703 tList<tLayer>& layers = layerLists[face]; 
704 for (int mip = 0; mip < NumMipmapLayers; mip++) 
705
706 layers.Append( item: Layers[mip][face] ); 
707 Layers[mip][face] = nullptr
708
709 faceCount++; 
710
711 
712 Clear(); 
713 return faceCount
714
715 
716 
717int tImageKTX::GetCubemapLayers(tList<tLayer> layerLists[tFaceIndex_NumFaces], uint32 faceFlags) const 
718
719 if (!IsValid() || !IsCubemap() || !faceFlags
720 return 0
721 
722 int faceCount = 0
723 for (int face = 0; face < tFaceIndex_NumFaces; face++) 
724
725 uint32 faceFlag = 1 << face
726 if (!(faceFlag & faceFlags)) 
727 continue
728 
729 tList<tLayer>& layers = layerLists[face]; 
730 for (int mip = 0; mip < NumMipmapLayers; mip++) 
731 layers.Append( item: Layers[mip][face] ); 
732 
733 faceCount++; 
734
735 
736 return faceCount
737
738 
739 
740bool tImageKTX::Load(const tString& ktxFile, const LoadParams& loadParams
741
742 Clear(); 
743 Filename = ktxFile
744 tSystem::tFileType fileType = tSystem::tGetFileType(file: ktxFile); 
745 if ((fileType != tSystem::tFileType::KTX) && (fileType != tSystem::tFileType::KTX2)) 
746
747 SetStateBit(StateBit::Fatal_IncorrectFileType); 
748 return false
749
750 
751 if (!tSystem::tFileExists(file: ktxFile)) 
752
753 SetStateBit(StateBit::Fatal_FileDoesNotExist); 
754 return false
755
756 
757 #if 0 
758 int ktxSizeBytes = 0
759 uint8* ktxData = (uint8*)tSystem::tLoadFile(ktxFile, 0, &ktxSizeBytes); 
760 bool success = Load(ktxData, ktxSizeBytes, loadParams); 
761 delete[] ktxData; 
762 return success; 
763 #endif 
764 
765 ktx_error_code_e result = KTX_SUCCESS
766 ktxTexture* texture = nullptr
767 result = ktxTexture_CreateFromNamedFile(filename: ktxFile.Chr(), createFlags: KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, newTex: &texture); 
768 if (!texture || (result != KTX_SUCCESS)) 
769
770 SetStateBit(StateBit::Fatal_CouldNotParseFile); 
771 return false
772
773 
774 return LoadFromTexture(texture, paramsIn: loadParams); 
775
776 
777 
778bool tImageKTX::Load(const uint8* ktxData, int ktxSizeBytes, const LoadParams& paramsIn
779
780 Clear(); 
781 
782 ktx_error_code_e result = KTX_SUCCESS
783 ktxTexture* texture = nullptr
784 result = ktxTexture_CreateFromMemory(bytes: ktxData, size: ktxSizeBytes, createFlags: KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, newTex: &texture); 
785 if (!texture || (result != KTX_SUCCESS)) 
786
787 SetStateBit(StateBit::Fatal_CouldNotParseFile); 
788 return false
789
790 
791 return LoadFromTexture(texture, paramsIn); 
792
793 
794 
795bool tImageKTX::LoadFromTexture(ktxTexture* texture, const LoadParams& paramsIn
796
797 tAssert(texture); 
798 LoadParams params(paramsIn); 
799 ktx_error_code_e result = KTX_SUCCESS
800 
801 NumImages = texture->numFaces; // Number of faces. 1 or 6 for cubemaps. 
802 int numLayers = texture->numLayers; // Number of array layers. I believe this will be > 1 for 3D textures that are made of an array of layers. 
803 NumMipmapLayers = texture->numLevels; // Mipmap levels. 
804 int numDims = texture->numDimensions; // 1D, 2D, or 3D. 
805 int mainWidth = texture->baseWidth
806 int mainHeight = texture->baseHeight
807 bool isArray = texture->isArray
808 bool isCompressed = texture->isCompressed
809 
810 if ((NumMipmapLayers <= 0) || (numDims != 2) || (mainWidth <= 0) || (mainHeight <= 0)) 
811
812 ktxTexture_Destroy(texture); 
813 SetStateBit(StateBit::Fatal_InvalidDimensions); 
814 return false
815
816 
817 if (NumMipmapLayers > MaxMipmapLayers
818
819 ktxTexture_Destroy(texture); 
820 SetStateBit(StateBit::Fatal_MaxNumMipmapLevelsExceeded); 
821 return false
822
823 
824 IsCubeMap = (NumImages == 6); 
825 
826 // We need to determine the pixel-format of the data. To do this we first need to know if we are dealing with 
827 // a ktx1 (OpenGL-style) or ktx2 (Vulkan-style) file. 
828 ktxTexture1* ktx1 = (texture->classId == ktxTexture1_c) ? (ktxTexture1*)texture : nullptr
829 ktxTexture2* ktx2 = (texture->classId == ktxTexture2_c) ? (ktxTexture2*)texture : nullptr
830 if (!ktx1 && !ktx2
831
832 ktxTexture_Destroy(texture); 
833 SetStateBit(StateBit::Fatal_CorruptedFile); 
834 return false
835
836 
837 tSystem::tFileType fileType = tSystem::tGetFileType(file: Filename); 
838 if (ktx1
839
840 tKTX::GetFormatInfo_FromGLFormat(format&: PixelFormatSrc, profile&: ColourProfileSrc, alphaMode&: AlphaMode, chanType&: ChannelType, glType: ktx1->glType, glFormat: ktx1->glFormat, glInternalFormat: ktx1->glInternalformat); 
841 if (fileType == tSystem::tFileType::KTX2
842 SetStateBit(StateBit::Conditional_ExtVersionMismatch); 
843
844 else if (ktx2
845
846 tKTX::GetFormatInfo_FromVKFormat(format&: PixelFormatSrc, profile&: ColourProfileSrc, alphaMode&: AlphaMode, chanType&: ChannelType, vkFormat: ktx2->vkFormat); 
847 if (fileType == tSystem::tFileType::KTX
848 SetStateBit(StateBit::Conditional_ExtVersionMismatch); 
849
850 PixelFormat = PixelFormatSrc
851 ColourProfile = ColourProfileSrc
852 
853 // From now on we should just be using the PixelFormat to decide what to do next. 
854 if (PixelFormat == tPixelFormat::Invalid
855
856 ktxTexture_Destroy(texture); 
857 SetStateBit(StateBit::Fatal_PixelFormatNotSupported); 
858 return false
859
860 
861 if (tIsBCFormat(format: PixelFormat)) 
862
863 if ((params.Flags & LoadFlag_CondMultFourDim) && ((mainWidth%4) || (mainHeight%4))) 
864 SetStateBit(StateBit::Conditional_DimNotMultFourBC); 
865 if ((params.Flags & LoadFlag_CondPowerTwoDim) && (!tMath::tIsPower2(v: mainWidth) || !tMath::tIsPower2(v: mainHeight))) 
866 SetStateBit(StateBit::Conditional_DimNotMultFourBC); 
867
868 
869 bool reverseRowOrderRequested = params.Flags & LoadFlag_ReverseRowOrder
870 RowReversalOperationPerformed = false
871 
872 // For images whose height is not a multiple of the block size it makes it tricky when deoompressing to do 
873 // the more efficient row reversal here, so we defer it. Packed formats have a block height of 1. Only BC 
874 // and astc have non-unity block dimensins. 
875 bool doRowReversalBeforeDecode = false
876 if (reverseRowOrderRequested
877
878 bool canDo = true
879 for (int image = 0; image < NumImages; image++) 
880
881 int height = mainHeight
882 for (int layer = 0; layer < NumMipmapLayers; layer++) 
883
884 if (!CanReverseRowData(PixelFormat, height)) 
885
886 canDo = false
887 break
888
889 height /= 2; if (height < 1) height = 1
890
891 if (!canDo
892 break
893
894 doRowReversalBeforeDecode = canDo
895
896 
897 for (int image = 0; image < NumImages; image++) 
898
899 int width = mainWidth
900 int height = mainHeight
901 for (int layer = 0; layer < NumMipmapLayers; layer++) 
902
903 size_t offset
904 result = ktxTexture_GetImageOffset(texture, layer, 0, image, &offset); 
905 if (result != KTX_SUCCESS
906
907 ktxTexture_Destroy(texture); 
908 SetStateBit(StateBit::Fatal_InvalidDataOffset); 
909 return false
910
911 
912 uint8* currPixelData = ktxTexture_GetData(This: texture) + offset
913 int numBytes = 0
914 if (tImage::tIsBCFormat(format: PixelFormat) || tImage::tIsASTCFormat(format: PixelFormat) || tImage::tIsPackedFormat(format: PixelFormat) || tImage::tIsPVRFormat(format: PixelFormat)) 
915
916 // It's a block format (BC/DXTn or ASTC). Each block encodes a 4x4 up to 12x12 square of pixels. DXT2,3,4,5 and BC 6,7 use 128 
917 // bits per block. DXT1 and DXT1A (BC1) use 64bits per block. ASTC always uses 128 bits per block but it's not always 4x4. 
918 // Packed formats are considered to have a block width and height of 1. 
919 int blockW = tGetBlockWidth(PixelFormat); 
920 int blockH = tGetBlockHeight(PixelFormat); 
921 int bytesPerBlock = tImage::tGetBytesPerBlock(PixelFormat); 
922 tAssert(bytesPerBlock > 0); 
923 int numBlocksW = tGetNumBlocks(blockWH: blockW, imageWH: width); 
924 int numBlocksH = tGetNumBlocks(blockWH: blockH, imageWH: height); 
925 int numBlocks = numBlocksW*numBlocksH
926 numBytes = numBlocks * bytesPerBlock
927 
928 // Here's where we possibly modify the opaque DXT1 texture to be DXT1A if there are blocks with binary 
929 // transparency. We only bother checking the main layer. If it's opaque we assume all the others are too. 
930 if ((layer == 0) && (PixelFormat == tPixelFormat::BC1DXT1) && tImage::DoBC1BlocksHaveBinaryAlpha(blocks: (tImage::BC1Block*)currPixelData, numBlocks)) 
931 PixelFormat = PixelFormatSrc = tPixelFormat::BC1DXT1A
932 
933 // KTX files store textures upside down. In the OpenGL RH coord system, the lower left of the texture 
934 // is the origin and consecutive rows go up. For this reason we need to read each row of blocks from 
935 // the top to the bottom row. We also need to flip the rows within the 4x4 block by flipping the lookup 
936 // tables. This should be fairly fast as there is no encoding or encoding going on. Width and height 
937 // will go down to 1x1, which will still use a 4x4 DXT pixel-block. 
938 if (doRowReversalBeforeDecode
939
940 uint8* reversedPixelData = tImage::CreateReversedRowData(pixelData: currPixelData, pixelDataFormat: PixelFormat, numBlocksW, numBlocksH); 
941 tAssert(reversedPixelData); 
942 
943 // We can simply get the layer to steal the memory (the last true arg). 
944 Layers[layer][image] = new tLayer(PixelFormat, width, height, reversedPixelData, true); 
945
946 else 
947
948 // Not reversing. Use the currPixelData. 
949 Layers[layer][image] = new tLayer(PixelFormat, width, height, (uint8*)currPixelData); 
950
951 
952 tAssert(Layers[layer][image]->GetDataSize() == numBytes); 
953
954 else 
955
956 // Unsupported pixel format. 
957 Clear(); 
958 SetStateBit(StateBit::Fatal_PixelFormatNotSupported); 
959 return false
960
961 
962 currPixelData += numBytes
963 width /= 2; tMath::tiClampMin(val&: width, min: 1); 
964 height /= 2; tMath::tiClampMin(val&: height, min: 1); 
965
966
967 
968 if (doRowReversalBeforeDecode
969 RowReversalOperationPerformed = true
970 
971 // Not asked to decode. We're basically done. 
972 if (!(params.Flags & LoadFlag_Decode)) 
973
974 if (reverseRowOrderRequested && !RowReversalOperationPerformed
975 SetStateBit(StateBit::Conditional_CouldNotFlipRows); 
976 
977 SetStateBit(StateBit::Valid); 
978 tAssert(IsValid()); 
979 return true
980
981 
982 // Decode to 32-bit RGBA. 
983 // Spread only applies to single-channel (R-only or L-only) formats. 
984 bool spread = params.Flags & LoadFlag_SpreadLuminance
985 
986 // The gamma-compression load flags only apply when decoding. If the gamma mode is auto, we determine here 
987 // whether to apply sRGB compression. If the space is linear and a format that often encodes colours, we apply it. 
988 if (params.Flags & LoadFlag_AutoGamma
989
990 // Clear all related flags. 
991 params.Flags &= ~(LoadFlag_AutoGamma | LoadFlag_SRGBCompression | LoadFlag_GammaCompression); 
992 if (tMath::tIsProfileLinearInRGB(profile: ColourProfileSrc)) 
993
994 // Just cuz it's linear doesn't mean we want to gamma transform. Some formats should be kept linear. 
995 if 
996
997 (PixelFormatSrc != tPixelFormat::A8) && (PixelFormatSrc != tPixelFormat::A8L8) && 
998 (PixelFormatSrc != tPixelFormat::BC4ATI1U) && (PixelFormatSrc != tPixelFormat::BC4ATI1S) && 
999 (PixelFormatSrc != tPixelFormat::BC5ATI2U) && (PixelFormatSrc != tPixelFormat::BC5ATI2S
1000
1001
1002 params.Flags |= LoadFlag_SRGBCompression
1003
1004
1005
1006 
1007 bool didRowReversalAfterDecode = false
1008 for (int image = 0; image < NumImages; image++) 
1009
1010 for (int layerNum = 0; layerNum < NumMipmapLayers; layerNum++) 
1011
1012 tLayer* layer = Layers[layerNum][image]; 
1013 int w = layer->Width
1014 int h = layer->Height
1015 
1016 // At the end of decoding _either_ decoded4b _or_ decoded4f will be valid, not both. 
1017 // The decoded4b format used for LDR images. 
1018 // The decoded4f format used for HDR images. 
1019 tColour4b* decoded4b = nullptr
1020 tColour4f* decoded4f = nullptr
1021 DecodeResult result = DecodePixelData 
1022
1023 layer->PixelFormat, data: layer->Data, dataSize: layer->GetDataSize(), 
1024 width: w, height: h, dstLDR&: decoded4b, dstHDR&: decoded4f 
1025 ); 
1026 
1027 if (result != DecodeResult::Success
1028
1029 Clear(); 
1030 switch (result
1031
1032 case DecodeResult::PackedDecodeError: SetStateBit(StateBit::Fatal_PackedDecodeError); break
1033 case DecodeResult::BlockDecodeError: SetStateBit(StateBit::Fatal_BCDecodeError); break
1034 case DecodeResult::ASTCDecodeError: SetStateBit(StateBit::Fatal_ASTCDecodeError); break
1035 default: SetStateBit(StateBit::Fatal_PixelFormatNotSupported); break
1036
1037 return false
1038
1039 
1040 // Apply any decode flags. 
1041 tAssert(decoded4b || decoded4f); 
1042 bool flagTone = (params.Flags & tImageKTX::LoadFlag_ToneMapExposure) ? true : false
1043 bool flagSRGB = (params.Flags & tImageKTX::LoadFlag_SRGBCompression) ? true : false
1044 bool flagGama = (params.Flags & tImageKTX::LoadFlag_GammaCompression)? true : false
1045 if (decoded4f && (flagTone || flagSRGB || flagGama)) 
1046
1047 for (int p = 0; p < w*h; p++) 
1048
1049 tColour4f& colour = decoded4f[p]; 
1050 if (flagTone
1051 colour.TonemapExposure(exposure: params.Exposure, chans: tCompBit_RGB); 
1052 if (flagSRGB
1053 colour.LinearToSRGB(chans: tCompBit_RGB); 
1054 if (flagGama
1055 colour.LinearToGamma(gamma: params.Gamma, chans: tCompBit_RGB); 
1056
1057
1058 if (decoded4b && (flagSRGB || flagGama)) 
1059
1060 for (int p = 0; p < w*h; p++) 
1061
1062 tColour4f colour(decoded4b[p]); 
1063 if (flagSRGB
1064 colour.LinearToSRGB(chans: tCompBit_RGB); 
1065 if (flagGama
1066 colour.LinearToGamma(gamma: params.Gamma, chans: tCompBit_RGB); 
1067 decoded4b[p].SetR(colour.R); 
1068 decoded4b[p].SetG(colour.G); 
1069 decoded4b[p].SetB(colour.B); 
1070
1071
1072 
1073 // Update the layer with the 32-bit RGBA decoded data. If the data was HDR (float) 
1074 // convert it to 32 bit. Start by getting ride of the existing layer pixel data. 
1075 delete[] layer->Data
1076 if (decoded4f
1077
1078 tAssert(!decoded4b); 
1079 decoded4b = new tColour4b[w*h]; 
1080 for (int p = 0; p < w*h; p++) 
1081 decoded4b[p].Set(decoded4f[p]); 
1082 delete[] decoded4f
1083
1084 
1085 // Possibly spread the L/Red channel. 
1086 if (spread && tIsLuminanceFormat(format: layer->PixelFormat)) 
1087
1088 for (int p = 0; p < w*h; p++) 
1089
1090 decoded4b[p].G = decoded4b[p].R
1091 decoded4b[p].B = decoded4b[p].R
1092
1093
1094 
1095 layer->Data = (uint8*)decoded4b
1096 layer->PixelFormat = tPixelFormat::R8G8B8A8
1097 
1098 // We've got one more chance to reverse the rows here (if we still need to) because we were asked to decode. 
1099 if (reverseRowOrderRequested && !RowReversalOperationPerformed && (layer->PixelFormat == tPixelFormat::R8G8B8A8)) 
1100
1101 // This shouldn't ever fail. Too easy to reverse RGBA 32-bit. 
1102 uint8* reversedRowData = tImage::CreateReversedRowData(pixelData: layer->Data, pixelDataFormat: layer->PixelFormat, numBlocksW: w, numBlocksH: h); 
1103 tAssert(reversedRowData); 
1104 delete[] layer->Data
1105 layer->Data = reversedRowData
1106 didRowReversalAfterDecode = true
1107
1108 
1109 if ((params.Flags & LoadFlag_SwizzleBGR2RGB) && (layer->PixelFormat == tPixelFormat::R8G8B8A8)) 
1110
1111 for (int xy = 0; xy < w*h; xy++) 
1112
1113 tColour4b& col = ((tColour4b*)layer->Data)[xy]; 
1114 tStd::tSwap(a&: col.R, b&: col.B); 
1115
1116
1117
1118
1119 
1120 if (reverseRowOrderRequested && !RowReversalOperationPerformed && didRowReversalAfterDecode
1121 RowReversalOperationPerformed = true
1122 
1123 if (params.Flags & LoadFlag_SRGBCompression) ColourProfile = tColourProfile::LDRsRGB_LDRlA
1124 if (params.Flags & LoadFlag_GammaCompression) ColourProfile = tColourProfile::LDRgRGB_LDRlA
1125 
1126 // All images decoded. Can now set the object's pixel format. We do _not_ set the PixelFormatSrc here! 
1127 PixelFormat = tPixelFormat::R8G8B8A8
1128 
1129 if (reverseRowOrderRequested && !RowReversalOperationPerformed
1130 SetStateBit(StateBit::Conditional_CouldNotFlipRows); 
1131 
1132 ktxTexture_Destroy(texture); 
1133 SetStateBit(StateBit::Valid); 
1134 tAssert(IsValid()); 
1135 return true
1136
1137 
1138 
1139const char* tImageKTX::GetStateDesc(StateBit state
1140
1141 return StateDescriptions[int(state)]; 
1142
1143 
1144 
1145const char* tImageKTX::StateDescriptions[] = 
1146
1147 "Valid"
1148 "Conditional Valid. Image rows could not be flipped."
1149 "Conditional Valid. Image has dimension not multiple of four."
1150 "Conditional Valid. Image has dimension not power of two."
1151 "Conditional Valid. KTX extension doesn't match file version."
1152 "Fatal Error. File does not exist."
1153 "Fatal Error. Incorrect file type. Must be a KTX or KTX2 file."
1154 "Fatal Error. LibKTX could not parse file."
1155 "Fatal Error. KTX file corrupted."
1156 "Fatal Error. Incorrect Dimensions."
1157 "Fatal Error. KTX volume textures not supported."
1158 "Fatal Error. Unsupported pixel format."
1159 "Fatal Error. Invalid pixel data offset."
1160 "Fatal Error. Maximum number of mipmap levels exceeded."
1161 "Fatal Error. Unable to decode packed pixels."
1162 "Fatal Error. Unable to decode BC pixels."
1163 "Fatal Error. Unable to decode ASTC pixels." 
1164}; 
1165tStaticAssert(tNumElements(tImageKTX::StateDescriptions) == int(tImageKTX::StateBit::NumStateBits)); 
1166tStaticAssert(int(tImageKTX::StateBit::NumStateBits) <= int(tImageKTX::StateBit::MaxStateBits)); 
1167 
1168 
1169
1170