1/* -*- tab-width: 4; -*- */
2/* vi: set sw=2 ts=4 expandtab: */
3
4/*
5 * Copyright 2019-2020 The Khronos Group Inc.
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9/**
10 * @internal
11 * @file texture2.c
12 * @~English
13 *
14 * @brief ktxTexture2 implementation. Support for KTX2 format.
15 *
16 * @author Mark Callow, www.edgewise-consulting.com
17 */
18
19#if defined(_WIN32)
20#define _CRT_SECURE_NO_WARNINGS
21#endif
22
23#include <stdlib.h>
24#include <string.h>
25#include <zstd.h>
26#include <zstd_errors.h>
27#include <KHR/khr_df.h>
28
29#include "dfdutils/dfd.h"
30#include "ktx.h"
31#include "ktxint.h"
32#include "filestream.h"
33#include "memstream.h"
34#include "texture2.h"
35#include "unused.h"
36#include "vk_format.h"
37
38// FIXME: Test this #define and put it in a header somewhere.
39//#define IS_BIG_ENDIAN (1 == *(unsigned char *)&(const int){0x01000000ul})
40#define IS_BIG_ENDIAN 0
41
42struct ktxTexture_vtbl ktxTexture2_vtbl;
43struct ktxTexture_vtblInt ktxTexture2_vtblInt;
44
45#if !defined(BITFIELD_ORDER_FROM_MSB)
46// Most compilers, including all those tested so far, including clang, gcc
47// and msvc, order bitfields from the lsb so these struct declarations work.
48// Could this be because I've only tested on little-endian machines?
49// These are preferred as they are much easier to manually initialize
50// and verify.
51struct sampleType {
52 uint32_t bitOffset: 16;
53 uint32_t bitLength: 8;
54 uint32_t channelType: 8; // Includes qualifiers
55 uint32_t samplePosition0: 8;
56 uint32_t samplePosition1: 8;
57 uint32_t samplePosition2: 8;
58 uint32_t samplePosition3: 8;
59 uint32_t lower;
60 uint32_t upper;
61};
62
63struct BDFD {
64 uint32_t vendorId: 17;
65 uint32_t descriptorType: 15;
66 uint32_t versionNumber: 16;
67 uint32_t descriptorBlockSize: 16;
68 uint32_t model: 8;
69 uint32_t primaries: 8;
70 uint32_t transfer: 8;
71 uint32_t flags: 8;
72 uint32_t texelBlockDimension0: 8;
73 uint32_t texelBlockDimension1: 8;
74 uint32_t texelBlockDimension2: 8;
75 uint32_t texelBlockDimension3: 8;
76 uint32_t bytesPlane0: 8;
77 uint32_t bytesPlane1: 8;
78 uint32_t bytesPlane2: 8;
79 uint32_t bytesPlane3: 8;
80 uint32_t bytesPlane4: 8;
81 uint32_t bytesPlane5: 8;
82 uint32_t bytesPlane6: 8;
83 uint32_t bytesPlane7: 8;
84 struct sampleType samples[6];
85};
86
87struct BDFD e5b9g9r9_ufloat_comparator = {
88 .vendorId = 0,
89 .descriptorType = 0,
90 .versionNumber = 2,
91 .descriptorBlockSize = sizeof(struct BDFD),
92 .model = KHR_DF_MODEL_RGBSDA,
93 .primaries = KHR_DF_PRIMARIES_BT709,
94 .transfer = KHR_DF_TRANSFER_LINEAR,
95 .flags = KHR_DF_FLAG_ALPHA_STRAIGHT,
96 .texelBlockDimension0 = 0,
97 .texelBlockDimension1 = 0,
98 .texelBlockDimension2 = 0,
99 .texelBlockDimension3 = 0,
100 .bytesPlane0 = 4,
101 .bytesPlane1 = 0,
102 .bytesPlane2 = 0,
103 .bytesPlane3 = 0,
104 .bytesPlane4 = 0,
105 .bytesPlane5 = 0,
106 .bytesPlane6 = 0,
107 .bytesPlane7 = 0,
108 // gcc likes this way. It does not like, e.g.,
109 // .samples[0].bitOffset = 0, etc. which is accepted by both clang & msvc.
110 // I find the standards docs impenetrable so I don't know which is correct.
111 .samples[0] = {
112 .bitOffset = 0,
113 .bitLength = 8,
114 .channelType = KHR_DF_CHANNEL_RGBSDA_RED,
115 .samplePosition0 = 0,
116 .samplePosition1 = 0,
117 .samplePosition2 = 0,
118 .samplePosition3 = 0,
119 .lower = 0,
120 .upper = 8448,
121 },
122 .samples[1] = {
123 .bitOffset = 27,
124 .bitLength = 4,
125 .channelType = KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
126 .samplePosition0 = 0,
127 .samplePosition1 = 0,
128 .samplePosition2 = 0,
129 .samplePosition3 = 0,
130 .lower = 15,
131 .upper = 31,
132 },
133 .samples[2] = {
134 .bitOffset = 9,
135 .bitLength = 8,
136 .channelType = KHR_DF_CHANNEL_RGBSDA_GREEN,
137 .samplePosition0 = 0,
138 .samplePosition1 = 0,
139 .samplePosition2 = 0,
140 .samplePosition3 = 0,
141 .lower = 0,
142 .upper = 8448,
143 },
144 .samples[3] = {
145 .bitOffset = 27,
146 .bitLength = 4,
147 .channelType = KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
148 .samplePosition0 = 0,
149 .samplePosition1 = 0,
150 .samplePosition2 = 0,
151 .samplePosition3 = 0,
152 .lower = 15,
153 .upper = 31,
154 },
155 .samples[4] = {
156 .bitOffset = 18,
157 .bitLength = 8,
158 .channelType = KHR_DF_CHANNEL_RGBSDA_BLUE,
159 .samplePosition0 = 0,
160 .samplePosition1 = 0,
161 .samplePosition2 = 0,
162 .samplePosition3 = 0,
163 .lower = 0,
164 .upper = 8448,
165 },
166 .samples[5] = {
167 .bitOffset = 27,
168 .bitLength = 4,
169 .channelType = KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
170 .samplePosition0 = 0,
171 .samplePosition1 = 0,
172 .samplePosition2 = 0,
173 .samplePosition3 = 0,
174 .lower = 15,
175 .upper = 31,
176 }
177};
178#else
179// For compilers which order bitfields from the msb rather than lsb.
180#define shift(x,val) ((val) << KHR_DF_SHIFT_ ## x)
181#define sampleshift(x,val) ((val) << KHR_DF_SAMPLESHIFT_ ## x)
182#define e5b9g9r9_bdbwordcount KHR_DFDSIZEWORDS(6)
183ktx_uint32_t e5b9g9r9_ufloat_comparator[e5b9g9r9_bdbwordcount] = {
184 0, // descriptorType & vendorId
185 shift(DESCRIPTORBLOCKSIZE, e5b9g9r9_bdbwordcount * sizeof(ktx_uint32_t)) | shift(VERSIONNUMBER, 2),
186 // N.B. Allow various values of primaries, transfer & flags
187 shift(FLAGS, KHR_DF_FLAG_ALPHA_STRAIGHT) | shift(TRANSFER, KHR_DF_TRANSFER_LINEAR) | shift(PRIMARIES, KHR_DF_PRIMARIES_BT709) | shift(MODEL, KHR_DF_MODEL_RGBSDA),
188 0, // texelBlockDimension3~0
189 shift(BYTESPLANE0, 4), // All other bytesPlane fields are 0.
190 0, // bytesPlane7~4
191 sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 0),
192 0, // samplePosition3~0
193 0, // sampleLower
194 8448, // sampleUpper
195 sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),
196 0, // samplePosition3~0
197 15, // sampleLower
198 31, // sampleUpper
199 sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 9),
200 0, // samplePosition3~0
201 0, // sampleLower
202 8448, // sampleUpper
203 sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),
204 0, // samplePosition3~0
205 15, // sampleLower
206 31, // sampleUpper
207 sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 18),
208 0, // samplePosition3~0
209 0, // sampleLower
210 8448, // sampleUpper
211 sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),
212 0, // samplePosition3~0
213 15, // sampleLower
214 31, // sampleUpper
215};
216#endif
217
218/**
219* @private
220* @~English
221* @brief Initialize a ktxFormatSize object from the info in a DFD.
222*
223* This is used instead of referring to the DFD directly so code dealing
224* with format info can be common to KTX 1 & 2.
225*
226* @param[in] This pointer the ktxTexture2 whose DFD to use.
227* @param[in] fi pointer to the ktxFormatSize object to initialize.
228*
229* @return KTX_TRUE on success, otherwise KTX_FALSE.
230*/
231bool
232ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd)
233{
234 uint32_t* pBdb = pDfd + 1;
235
236 // Check the DFD is of the expected type and version.
237 if (*pBdb != 0) {
238 // Either decriptorType or vendorId is not 0
239 return false;
240 }
241 if (KHR_DFDVAL(pBdb, VERSIONNUMBER) != KHR_DF_VERSIONNUMBER_1_3) {
242 return false;
243 }
244
245 // DFD has supported type and version. Process it.
246 This->blockWidth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION0) + 1;
247 This->blockHeight = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION1) + 1;
248 This->blockDepth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION2) + 1;
249 This->blockSizeInBits = KHR_DFDVAL(pBdb, BYTESPLANE0) * 8;
250 This->paletteSizeInBits = 0; // No paletted formats in ktx v2.
251 This->flags = 0;
252 This->minBlocksX = This->minBlocksY = 1;
253 if (KHR_DFDVAL(pBdb, MODEL) >= KHR_DF_MODEL_DXT1A) {
254 // A block compressed format. Entire block is a single sample.
255 This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT;
256 if (KHR_DFDVAL(pBdb, MODEL) == KHR_DF_MODEL_PVRTC) {
257 This->minBlocksX = This->minBlocksY = 2;
258 }
259 } else {
260 // An uncompressed format.
261
262 // Special case depth & depth stencil formats
263 if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_DEPTH) {
264 if (KHR_DFDSAMPLECOUNT(pBdb) == 1) {
265 This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;
266 } else if (KHR_DFDSAMPLECOUNT(pBdb) == 2) {
267 This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;
268 This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;
269 This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
270 } else {
271 return false;
272 }
273 } else if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_STENCIL) {
274 This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;
275 } else if (KHR_DFDSAMPLECOUNT(pBdb) == 6
276#if !defined(BITFIELD_ORDER_FROM_MSB)
277 && !memcmp(((uint32_t*)&e5b9g9r9_ufloat_comparator) + KHR_DF_WORD_TEXELBLOCKDIMENSION0, &pBdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0], sizeof(e5b9g9r9_ufloat_comparator)-(KHR_DF_WORD_TEXELBLOCKDIMENSION0)*sizeof(uint32_t))) {
278#else
279 && !memcmp(&e5b9g9r9_ufloat_comparator[KHR_DF_WORD_TEXELBLOCKDIMENSION0], &pBdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0], sizeof(e5b9g9r9_ufloat_comparator)-(KHR_DF_WORD_TEXELBLOCKDIMENSION0)*sizeof(uint32_t))) {
280#endif
281 // Special case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 as interpretDFD
282 // only handles "simple formats", i.e. where channels are described
283 // in contiguous bits.
284 This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
285 } else {
286 InterpretedDFDChannel rgba[4];
287 uint32_t wordBytes;
288 enum InterpretDFDResult result;
289
290 result = interpretDFD(pDfd, &rgba[0], &rgba[1], &rgba[2], &rgba[3],
291 &wordBytes);
292 if (result >= i_UNSUPPORTED_ERROR_BIT)
293 return false;
294 if (result & i_PACKED_FORMAT_BIT)
295 This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
296 }
297 }
298 if (This->blockSizeInBits == 0) {
299 // The DFD shows a supercompressed texture. Complete the ktxFormatSize
300 // struct by figuring out the post inflation value for bytesPlane0.
301 // Setting it here simplifies stuff later in this file. Setting the
302 // post inflation block size here will not cause any problems for
303 // the following reasons. (1) in v2 files levelIndex is always used to
304 // calculate data size and, of course, for the level offsets. (2) Finer
305 // grain access to supercompressed data than levels is not possible.
306 uint32_t blockByteLength;
307 recreateBytesPlane0FromSampleInfo(pDfd, &blockByteLength);
308 This->blockSizeInBits = blockByteLength * 8;
309 }
310 return true;
311}
312
313/**
314 * @private
315 * @~English
316 * @brief Create a DFD for a VkFormat.
317 *
318 * This KTX-specific function adds support for combined depth stencil formats
319 * which are not supported by @e dfdutils' @c vk2dfd function because they
320 * are not seen outside a Vulkan device. KTX has its own definitions for
321 * these that enable uploading, with some effort.
322 *
323 * @param[in] vkFormat the format for which to create a DFD.
324 */
325static uint32_t*
326ktxVk2dfd(ktx_uint32_t vkFormat)
327{
328 switch(vkFormat) {
329 case VK_FORMAT_D16_UNORM_S8_UINT:
330 // 2 16-bit words. D16 in the first. S8 in the 8 LSBs of the second.
331 return createDFDDepthStencil(16, 8, 4);
332 case VK_FORMAT_D24_UNORM_S8_UINT:
333 // 1 32-bit word. D24 in the MSBs. S8 in the LSBs.
334 return createDFDDepthStencil(24, 8, 4);
335 case VK_FORMAT_D32_SFLOAT_S8_UINT:
336 // 2 32-bit words. D32 float in the first word. S8 in LSBs of the
337 // second.
338 return createDFDDepthStencil(32, 8, 8);
339 default:
340 return vk2dfd(vkFormat);
341 }
342}
343
344/**
345 * @memberof ktxTexture2 @private
346 * @~English
347 * @brief Do the part of ktxTexture2 construction that is common to
348 * new textures and those constructed from a stream.
349 *
350 * @param[in] This pointer to a ktxTexture2-sized block of memory to
351 * initialize.
352 * @param[in] numLevels the number of levels the texture must have.
353 *
354 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
355 * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.
356 */
357static KTX_error_code
358ktxTexture2_constructCommon(ktxTexture2* This, ktx_uint32_t numLevels)
359{
360 assert(This != NULL);
361 ktx_size_t privateSize;
362
363 This->classId = ktxTexture2_c;
364 This->vtbl = &ktxTexture2_vtbl;
365 This->_protected->_vtbl = ktxTexture2_vtblInt;
366 privateSize = sizeof(ktxTexture2_private)
367 + sizeof(ktxLevelIndexEntry) * (numLevels - 1);
368 This->_private = (ktxTexture2_private*)malloc(privateSize);
369 if (This->_private == NULL) {
370 return KTX_OUT_OF_MEMORY;
371 }
372 memset(This->_private, 0, privateSize);
373 return KTX_SUCCESS;
374}
375
376/**
377 * @memberof ktxTexture2 @private
378 * @~English
379 * @brief Construct a new, empty, ktxTexture2.
380 *
381 * @param[in] This pointer to a ktxTexture2-sized block of memory to
382 * initialize.
383 * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with
384 * information describing the texture.
385 * @param[in] storageAllocation
386 * enum indicating whether or not to allocate storage
387 * for the texture images.
388 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
389 * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture or image data.
390 * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
391 * The request VkFormat is one of the
392 * prohibited formats.
393 */
394static KTX_error_code
395ktxTexture2_construct(ktxTexture2* This, ktxTextureCreateInfo* createInfo,
396 ktxTextureCreateStorageEnum storageAllocation)
397{
398 ktxFormatSize formatSize;
399 KTX_error_code result;
400
401 memset(This, 0, sizeof(*This));
402
403 if (createInfo->vkFormat != VK_FORMAT_UNDEFINED) {
404 This->pDfd = ktxVk2dfd(createInfo->vkFormat);
405 if (!This->pDfd)
406 return KTX_INVALID_VALUE; // Format is unknown or unsupported.
407
408#ifdef _DEBUG
409 // If this fires, an unsupported format or incorrect DFD
410 // has crept into vk2dfd.
411 assert(ktxFormatSize_initFromDfd(&formatSize, This->pDfd));
412#else
413 (void)ktxFormatSize_initFromDfd(&formatSize, This->pDfd);
414#endif
415
416 } else {
417 // TODO: Validate createInfo->pDfd.
418 This->pDfd = (ktx_uint32_t*)malloc(*createInfo->pDfd);
419 if (!This->pDfd)
420 return KTX_OUT_OF_MEMORY;
421 memcpy(This->pDfd, createInfo->pDfd, *createInfo->pDfd);
422 if (ktxFormatSize_initFromDfd(&formatSize, This->pDfd)) {
423 result = KTX_UNSUPPORTED_TEXTURE_TYPE;
424 goto cleanup;
425 }
426 }
427
428 result = ktxTexture_construct(ktxTexture(This), createInfo, &formatSize);
429
430 if (result != KTX_SUCCESS)
431 return result;
432 result = ktxTexture2_constructCommon(This, createInfo->numLevels);
433 if (result != KTX_SUCCESS)
434 goto cleanup;;
435
436 This->vkFormat = createInfo->vkFormat;
437
438 // Ideally we'd set all these things in ktxFormatSize_initFromDfd
439 // but This->_protected is not allocated until ktxTexture_construct;
440 if (This->isCompressed)
441 This->_protected->_typeSize = 1;
442 else if (formatSize.flags & KTX_FORMAT_SIZE_PACKED_BIT)
443 This->_protected->_typeSize = formatSize.blockSizeInBits / 8;
444 else if (formatSize.flags & (KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT)) {
445 if (createInfo->vkFormat == VK_FORMAT_D16_UNORM_S8_UINT)
446 This->_protected->_typeSize = 2;
447 else
448 This->_protected->_typeSize = 4;
449 } else {
450 // Unpacked and uncompressed
451 uint32_t numComponents;
452 getDFDComponentInfoUnpacked(This->pDfd, &numComponents,
453 &This->_protected->_typeSize);
454 }
455
456 This->supercompressionScheme = KTX_SS_NONE;
457
458 This->_private->_requiredLevelAlignment
459 = ktxTexture2_calcRequiredLevelAlignment(This);
460
461 // Create levelIndex. Offsets are from start of the KTX2 stream.
462 ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;
463
464 This->_private->_firstLevelFileOffset = 0;
465
466 for (ktx_uint32_t level = 0; level < This->numLevels; level++) {
467 levelIndex[level].uncompressedByteLength =
468 ktxTexture_calcLevelSize(ktxTexture(This), level,
469 KTX_FORMAT_VERSION_TWO);
470 levelIndex[level].byteLength =
471 levelIndex[level].uncompressedByteLength;
472 levelIndex[level].byteOffset =
473 ktxTexture_calcLevelOffset(ktxTexture(This), level);
474 }
475
476 // Allocate storage, if requested.
477 if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) {
478 This->dataSize
479 = ktxTexture_calcDataSizeTexture(ktxTexture(This));
480 This->pData = malloc(This->dataSize);
481 if (This->pData == NULL) {
482 result = KTX_OUT_OF_MEMORY;
483 goto cleanup;
484 }
485 }
486 return result;
487
488cleanup:
489 ktxTexture2_destruct(This);
490 return result;
491}
492
493/**
494 * @memberof ktxTexture2 @private
495 * @~English
496 * @brief Construct a ktxTexture by copying a source ktxTexture.
497 *
498 * @param[in] This pointer to a ktxTexture2-sized block of memory to
499 * initialize.
500 * @param[in] orig pointer to the source texture to copy.
501 *
502 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
503 *
504 * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.
505 */
506static KTX_error_code
507ktxTexture2_constructCopy(ktxTexture2* This, ktxTexture2* orig)
508{
509 KTX_error_code result;
510
511 memcpy(This, orig, sizeof(ktxTexture2));
512 // Zero all the pointers to make error handling easier
513 This->_protected = NULL;
514 This->_private = NULL;
515 This->pDfd = NULL;
516 This->kvData = NULL;
517 This->kvDataHead = NULL;
518 This->pData = NULL;
519
520 This->_protected =
521 (ktxTexture_protected*)malloc(sizeof(ktxTexture_protected));
522 if (!This->_protected)
523 return KTX_OUT_OF_MEMORY;
524 // Must come before memcpy of _protected so as to close an active stream.
525 if (!orig->pData && ktxTexture_isActiveStream((ktxTexture*)orig))
526 ktxTexture2_LoadImageData(orig, NULL, 0);
527 memcpy(This->_protected, orig->_protected, sizeof(ktxTexture_protected));
528
529 ktx_size_t privateSize = sizeof(ktxTexture2_private)
530 + sizeof(ktxLevelIndexEntry) * (orig->numLevels - 1);
531 This->_private = (ktxTexture2_private*)malloc(privateSize);
532 if (This->_private == NULL) {
533 result = KTX_OUT_OF_MEMORY;
534 goto cleanup;
535 }
536 memcpy(This->_private, orig->_private, privateSize);
537 if (orig->_private->_sgdByteLength > 0) {
538 This->_private->_supercompressionGlobalData
539 = (ktx_uint8_t*)malloc(orig->_private->_sgdByteLength);
540 if (!This->_private->_supercompressionGlobalData) {
541 result = KTX_OUT_OF_MEMORY;
542 goto cleanup;
543 }
544 memcpy(This->_private->_supercompressionGlobalData,
545 orig->_private->_supercompressionGlobalData,
546 orig->_private->_sgdByteLength);
547 }
548
549 This->pDfd = (ktx_uint32_t*)malloc(*orig->pDfd);
550 if (!This->pDfd) {
551 result = KTX_OUT_OF_MEMORY;
552 goto cleanup;
553 }
554 memcpy(This->pDfd, orig->pDfd, *orig->pDfd);
555
556 if (orig->kvDataHead) {
557 ktxHashList_ConstructCopy(&This->kvDataHead, orig->kvDataHead);
558 } else if (orig->kvData) {
559 This->kvData = (ktx_uint8_t*)malloc(orig->kvDataLen);
560 if (!This->kvData) {
561 result = KTX_OUT_OF_MEMORY;
562 goto cleanup;
563 }
564 memcpy(This->kvData, orig->kvData, orig->kvDataLen);
565 }
566
567 // Can't share the image data as the data pointer is exposed in the
568 // ktxTexture2 structure. Changing it to a ref-counted pointer would
569 // break code. Maybe that's okay as we're still pre-release. But,
570 // since this constructor will be mostly be used when transcoding
571 // supercompressed images, it is probably not too big a deal to make
572 // a copy of the data.
573 This->pData = (ktx_uint8_t*)malloc(This->dataSize);
574 if (This->pData == NULL) {
575 result = KTX_OUT_OF_MEMORY;
576 goto cleanup;
577 }
578 memcpy(This->pData, orig->pData, orig->dataSize);
579 return KTX_SUCCESS;
580
581cleanup:
582 if (This->_protected) free(This->_protected);
583 if (This->_private) {
584 if (This->_private->_supercompressionGlobalData)
585 free(This->_private->_supercompressionGlobalData);
586 free(This->_private);
587 }
588 if (This->pDfd) free (This->pDfd);
589 if (This->kvDataHead) ktxHashList_Destruct(&This->kvDataHead);
590
591 return result;
592}
593
594/**
595 * @memberof ktxTexture2 @private
596 * @~English
597 * @brief Construct a ktxTexture from a ktxStream reading from a KTX source.
598 *
599 * The KTX header, which must have been read prior to calling this, is passed
600 * to the function.
601 *
602 * The stream object is copied into the constructed ktxTexture2.
603 *
604 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
605 * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
606 * will minimize memory usage by allowing, for example, loading the images
607 * directly from the source into a Vulkan staging buffer.
608 *
609 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
610 * provided solely to enable implementation of the @e libktx v1 API on top of
611 * ktxTexture.
612 *
613 * If either KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT or
614 * KTX_TEXTURE_CREATE_RAW_KVDATA_BIT is set then the ktxTexture's orientation
615 * fields will be set to defaults even if the KTX source contains
616 * KTXorientation metadata.
617 *
618 * @param[in] This pointer to a ktxTexture2-sized block of memory to
619 * initialize.
620 * @param[in] pStream pointer to the stream to read.
621 * @param[in] pHeader pointer to a KTX header that has already been read from
622 * the stream.
623 * @param[in] createFlags bitmask requesting specific actions during creation.
624 *
625 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
626 *
627 * @exception KTX_FILE_DATA_ERROR
628 * Source data is inconsistent with the KTX
629 * specification.
630 * @exception KTX_FILE_READ_ERROR
631 * An error occurred while reading the source.
632 * @exception KTX_FILE_UNEXPECTED_EOF
633 * Not enough data in the source.
634 * @exception KTX_OUT_OF_MEMORY Not enough memory to load either the images or
635 * the key-value data.
636 * @exception KTX_UNKNOWN_FILE_FORMAT
637 * The source is not in KTX format.
638 * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
639 * The source describes a texture type not
640 * supported by OpenGL or Vulkan, e.g, a 3D array.
641 */
642KTX_error_code
643ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
644 KTX_header2* pHeader,
645 ktxTextureCreateFlags createFlags)
646{
647 ktxTexture2_private* private;
648 KTX_error_code result;
649 KTX_supplemental_info suppInfo;
650 ktxStream* stream;
651 ktx_size_t levelIndexSize;
652
653 assert(pHeader != NULL && pStream != NULL);
654
655 memset(This, 0, sizeof(*This));
656 result = ktxTexture_constructFromStream(ktxTexture(This), pStream,
657 createFlags);
658 if (result != KTX_SUCCESS)
659 return result;
660
661 result = ktxCheckHeader2_(pHeader, &suppInfo);
662 if (result != KTX_SUCCESS)
663 goto cleanup;
664 // ktxCheckHeader2_ has done the max(1, levelCount) on pHeader->levelCount.
665 result = ktxTexture2_constructCommon(This, pHeader->levelCount);
666 if (result != KTX_SUCCESS)
667 goto cleanup;
668 private = This->_private;
669
670 stream = ktxTexture2_getStream(This);
671
672 /*
673 * Initialize from pHeader->info.
674 */
675 This->vkFormat = pHeader->vkFormat;
676 This->supercompressionScheme = pHeader->supercompressionScheme;
677
678 This->_protected->_typeSize = pHeader->typeSize;
679 // Can these be done by a ktxTexture_constructFromStream?
680 This->numDimensions = suppInfo.textureDimension;
681 This->baseWidth = pHeader->pixelWidth;
682 assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4);
683 switch (suppInfo.textureDimension) {
684 case 1:
685 This->baseHeight = This->baseDepth = 1;
686 break;
687 case 2:
688 This->baseHeight = pHeader->pixelHeight;
689 This->baseDepth = 1;
690 break;
691 case 3:
692 This->baseHeight = pHeader->pixelHeight;
693 This->baseDepth = pHeader->pixelDepth;
694 break;
695 }
696 if (pHeader->layerCount > 0) {
697 This->numLayers = pHeader->layerCount;
698 This->isArray = KTX_TRUE;
699 } else {
700 This->numLayers = 1;
701 This->isArray = KTX_FALSE;
702 }
703 This->numFaces = pHeader->faceCount;
704 if (pHeader->faceCount == 6)
705 This->isCubemap = KTX_TRUE;
706 else
707 This->isCubemap = KTX_FALSE;
708 // ktxCheckHeader2_ does the max(1, levelCount) and sets
709 // suppInfo.generateMipmaps when it was originally 0.
710 This->numLevels = pHeader->levelCount;
711 This->generateMipmaps = suppInfo.generateMipmaps;
712
713 // Read level index
714 levelIndexSize = sizeof(ktxLevelIndexEntry) * This->numLevels;
715 result = stream->read(stream, &private->_levelIndex, levelIndexSize);
716 if (result != KTX_SUCCESS)
717 goto cleanup;
718 // Rebase index to start of data and save file offset.
719 private->_firstLevelFileOffset
720 = private->_levelIndex[This->numLevels-1].byteOffset;
721 for (ktx_uint32_t level = 0; level < This->numLevels; level++) {
722 private->_levelIndex[level].byteOffset
723 -= private->_firstLevelFileOffset;
724 }
725
726 // Read DFD
727 This->pDfd =
728 (ktx_uint32_t*)malloc(pHeader->dataFormatDescriptor.byteLength);
729 if (!This->pDfd) {
730 result = KTX_OUT_OF_MEMORY;
731 goto cleanup;
732 }
733 result = stream->read(stream, This->pDfd,
734 pHeader->dataFormatDescriptor.byteLength);
735 if (result != KTX_SUCCESS)
736 goto cleanup;
737
738 if (!ktxFormatSize_initFromDfd(&This->_protected->_formatSize, This->pDfd)) {
739 result = KTX_UNSUPPORTED_TEXTURE_TYPE;
740 goto cleanup;
741 }
742 This->isCompressed = (This->_protected->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);
743
744 if (This->supercompressionScheme == KTX_SS_BASIS_LZ
745 && KHR_DFDVAL(This->pDfd + 1, MODEL) != KHR_DF_MODEL_ETC1S)
746 {
747 result = KTX_FILE_DATA_ERROR;
748 goto cleanup;
749 }
750
751 This->_private->_requiredLevelAlignment
752 = ktxTexture2_calcRequiredLevelAlignment(This);
753
754 // Make an empty hash list.
755 ktxHashList_Construct(&This->kvDataHead);
756 // Load KVData.
757 if (pHeader->keyValueData.byteLength > 0) {
758 if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) {
759 ktx_uint32_t kvdLen = pHeader->keyValueData.byteLength;
760 ktx_uint8_t* pKvd;
761
762 pKvd = malloc(kvdLen);
763 if (pKvd == NULL) {
764 result = KTX_OUT_OF_MEMORY;
765 goto cleanup;
766 }
767
768 result = stream->read(stream, pKvd, kvdLen);
769 if (result != KTX_SUCCESS)
770 goto cleanup;
771
772 if (IS_BIG_ENDIAN) {
773 /* Swap the counts inside the key & value data. */
774 ktx_uint8_t* src = pKvd;
775 ktx_uint8_t* end = pKvd + kvdLen;
776 while (src < end) {
777 ktx_uint32_t keyAndValueByteSize = *((ktx_uint32_t*)src);
778 _ktxSwapEndian32(&keyAndValueByteSize, 1);
779 src += _KTX_PAD4(keyAndValueByteSize);
780 }
781 }
782
783 if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) {
784 char* orientationStr;
785 ktx_uint32_t orientationLen;
786 ktx_uint32_t animData[3];
787 ktx_uint32_t animDataLen;
788
789 result = ktxHashList_Deserialize(&This->kvDataHead,
790 kvdLen, pKvd);
791 free(pKvd);
792 if (result != KTX_SUCCESS) {
793 goto cleanup;
794 }
795
796 result = ktxHashList_FindValue(&This->kvDataHead,
797 KTX_ORIENTATION_KEY,
798 &orientationLen,
799 (void**)&orientationStr);
800 assert(result != KTX_INVALID_VALUE);
801 if (result == KTX_SUCCESS) {
802 // Length includes the terminating NUL.
803 if (orientationLen != This->numDimensions + 1) {
804 // There needs to be an entry for each dimension of
805 // the texture.
806 result = KTX_FILE_DATA_ERROR;
807 goto cleanup;
808 } else {
809 switch (This->numDimensions) {
810 case 3:
811 This->orientation.z = orientationStr[2];
812 FALLTHROUGH;
813 case 2:
814 This->orientation.y = orientationStr[1];
815 FALLTHROUGH;
816 case 1:
817 This->orientation.x = orientationStr[0];
818 }
819 }
820 } else {
821 result = KTX_SUCCESS; // Not finding orientation is okay.
822 }
823 result = ktxHashList_FindValue(&This->kvDataHead,
824 KTX_ANIMDATA_KEY,
825 &animDataLen,
826 (void**)animData);
827 assert(result != KTX_INVALID_VALUE);
828 if (result == KTX_SUCCESS) {
829 if (animDataLen != sizeof(animData)) {
830 result = KTX_FILE_DATA_ERROR;
831 goto cleanup;
832 }
833 if (This->isArray) {
834 This->isVideo = KTX_TRUE;
835 This->duration = animData[0];
836 This->timescale = animData[1];
837 This->loopcount = animData[2];
838 } else {
839 // animData is only valid for array textures.
840 result = KTX_FILE_DATA_ERROR;
841 goto cleanup;
842 }
843 } else {
844 result = KTX_SUCCESS; // Not finding video is okay.
845 }
846 } else {
847 This->kvDataLen = kvdLen;
848 This->kvData = pKvd;
849 }
850 } else {
851 stream->skip(stream, pHeader->keyValueData.byteLength);
852 }
853 }
854
855 if (pHeader->supercompressionGlobalData.byteLength > 0) {
856 // There could be padding here so seek to the next item.
857 (void)stream->setpos(stream,
858 pHeader->supercompressionGlobalData.byteOffset);
859
860 // Read supercompressionGlobalData
861 private->_supercompressionGlobalData =
862 (ktx_uint8_t*)malloc(pHeader->supercompressionGlobalData.byteLength);
863 if (!private->_supercompressionGlobalData) {
864 result = KTX_OUT_OF_MEMORY;
865 goto cleanup;
866 }
867 private->_sgdByteLength
868 = pHeader->supercompressionGlobalData.byteLength;
869 result = stream->read(stream, private->_supercompressionGlobalData,
870 private->_sgdByteLength);
871
872 if (result != KTX_SUCCESS)
873 goto cleanup;
874 }
875
876 // Calculate size of the image data. Level 0 is the last level in the data.
877 This->dataSize = private->_levelIndex[0].byteOffset
878 + private->_levelIndex[0].byteLength;
879
880 /*
881 * Load the images, if requested.
882 */
883 if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) {
884 result = ktxTexture2_LoadImageData(This, NULL, 0);
885 }
886 if (result != KTX_SUCCESS)
887 goto cleanup;
888
889 return result;
890
891cleanup:
892 ktxTexture2_destruct(This);
893 return result;
894}
895
896/**
897 * @memberof ktxTexture2 @private
898 * @~English
899 * @brief Construct a ktxTexture from a ktxStream reading from a KTX source.
900 *
901 * The stream object is copied into the constructed ktxTexture2.
902 *
903 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
904 * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
905 * will minimize memory usage by allowing, for example, loading the images
906 * directly from the source into a Vulkan staging buffer.
907 *
908 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
909 * provided solely to enable implementation of the @e libktx v1 API on top of
910 * ktxTexture.
911 *
912 * @param[in] This pointer to a ktxTexture2-sized block of memory to
913 * initialize.
914 * @param[in] pStream pointer to the stream to read.
915 * @param[in] createFlags bitmask requesting specific actions during creation.
916 *
917 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
918 *
919 * @exception KTX_FILE_READ_ERROR
920 * An error occurred while reading the source.
921 *
922 * For other exceptions see ktxTexture2_constructFromStreamAndHeader().
923 */
924static KTX_error_code
925ktxTexture2_constructFromStream(ktxTexture2* This, ktxStream* pStream,
926 ktxTextureCreateFlags createFlags)
927{
928 KTX_header2 header;
929 KTX_error_code result;
930
931 // Read header.
932 result = pStream->read(pStream, &header, KTX2_HEADER_SIZE);
933 if (result != KTX_SUCCESS)
934 return result;
935
936#if IS_BIG_ENDIAN
937 // byte swap the header
938#endif
939 return ktxTexture2_constructFromStreamAndHeader(This, pStream,
940 &header, createFlags);
941}
942
943/**
944 * @memberof ktxTexture2 @private
945 * @~English
946 * @brief Construct a ktxTexture from a stdio stream reading from a KTX source.
947 *
948 * See ktxTextureInt_constructFromStream for details.
949 *
950 * @note Do not close the stdio stream until you are finished with the texture
951 * object.
952 *
953 * @param[in] This pointer to a ktxTextureInt-sized block of memory to
954 * initialize.
955 * @param[in] stdioStream a stdio FILE pointer opened on the source.
956 * @param[in] createFlags bitmask requesting specific actions during creation.
957 *
958 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
959 *
960 * @exception KTX_INVALID_VALUE Either @p stdiostream or @p This is null.
961 *
962 * For other exceptions, see ktxTexture_constructFromStream().
963 */
964static KTX_error_code
965ktxTexture2_constructFromStdioStream(ktxTexture2* This, FILE* stdioStream,
966 ktxTextureCreateFlags createFlags)
967{
968 KTX_error_code result;
969 ktxStream stream;
970
971 if (stdioStream == NULL || This == NULL)
972 return KTX_INVALID_VALUE;
973
974 result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);
975 if (result == KTX_SUCCESS)
976 result = ktxTexture2_constructFromStream(This, &stream, createFlags);
977 return result;
978}
979
980/**
981 * @memberof ktxTexture2 @private
982 * @~English
983 * @brief Construct a ktxTexture from a named KTX file.
984 *
985 * See ktxTextureInt_constructFromStream for details.
986 *
987 * @param[in] This pointer to a ktxTextureInt-sized block of memory to
988 * initialize.
989 * @param[in] filename pointer to a char array containing the file name.
990 * @param[in] createFlags bitmask requesting specific actions during creation.
991 *
992 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
993 *
994 * @exception KTX_FILE_OPEN_FAILED The file could not be opened.
995 * @exception KTX_INVALID_VALUE @p filename is @c NULL.
996 *
997 * For other exceptions, see ktxTexture_constructFromStream().
998 */
999static KTX_error_code
1000ktxTexture2_constructFromNamedFile(ktxTexture2* This,
1001 const char* const filename,
1002 ktxTextureCreateFlags createFlags)
1003{
1004 KTX_error_code result;
1005 ktxStream stream;
1006 FILE* file;
1007
1008 if (This == NULL || filename == NULL)
1009 return KTX_INVALID_VALUE;
1010
1011 file = fopen(filename, "rb");
1012 if (!file)
1013 return KTX_FILE_OPEN_FAILED;
1014
1015 result = ktxFileStream_construct(&stream, file, KTX_TRUE);
1016 if (result == KTX_SUCCESS)
1017 result = ktxTexture2_constructFromStream(This, &stream, createFlags);
1018
1019 return result;
1020}
1021
1022/**
1023 * @memberof ktxTexture2 @private
1024 * @~English
1025 * @brief Construct a ktxTexture from KTX-formatted data in memory.
1026 *
1027 * See ktxTextureInt_constructFromStream for details.
1028 *
1029 * @param[in] This pointer to a ktxTextureInt-sized block of memory to
1030 * initialize.
1031 * @param[in] bytes pointer to the memory containing the serialized KTX data.
1032 * @param[in] size length of the KTX data in bytes.
1033 * @param[in] createFlags bitmask requesting specific actions during creation.
1034 *
1035 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
1036 *
1037 * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
1038 *
1039 * For other exceptions, see ktxTexture_constructFromStream().
1040 */
1041static KTX_error_code
1042ktxTexture2_constructFromMemory(ktxTexture2* This,
1043 const ktx_uint8_t* bytes, ktx_size_t size,
1044 ktxTextureCreateFlags createFlags)
1045{
1046 KTX_error_code result;
1047 ktxStream stream;
1048
1049 if (bytes == NULL || size == 0)
1050 return KTX_INVALID_VALUE;
1051
1052 result = ktxMemStream_construct_ro(&stream, bytes, size);
1053 if (result == KTX_SUCCESS)
1054 result = ktxTexture2_constructFromStream(This, &stream, createFlags);
1055
1056 return result;
1057}
1058
1059/**
1060 * @memberof ktxTexture2 @private
1061 * @~English
1062 * @brief Destruct a ktxTexture2, freeing and internal memory.
1063 *
1064 * @param[in] This pointer to a ktxTexture2-sized block of memory to
1065 * initialize.
1066 */
1067void
1068ktxTexture2_destruct(ktxTexture2* This)
1069{
1070 if (This->pDfd) free(This->pDfd);
1071 if (This->_private) {
1072 ktx_uint8_t* sgd = This->_private->_supercompressionGlobalData;
1073 if (sgd) free(sgd);
1074 free(This->_private);
1075 }
1076 ktxTexture_destruct(ktxTexture(This));
1077}
1078
1079/**
1080 * @memberof ktxTexture2
1081 * @ingroup writer
1082 * @~English
1083 * @brief Create a new empty ktxTexture2.
1084 *
1085 * The address of the newly created ktxTexture2 is written to the location
1086 * pointed at by @p newTex.
1087 *
1088 * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with
1089 * information describing the texture.
1090 * @param[in] storageAllocation
1091 * enum indicating whether or not to allocate storage
1092 * for the texture images.
1093 * @param[in,out] newTex pointer to a location in which store the address of
1094 * the newly created texture.
1095 *
1096 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
1097 *
1098 * @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a
1099 * valid OpenGL internal format value.
1100 * @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2
1101 * or 3.
1102 * @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in
1103 * @p createInfo is 0.
1104 * @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.
1105 * @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.
1106 * @exception KTX_INVALID_OPERATION
1107 * The <tt>base{Width,Height,Depth}</tt> specified
1108 * in @p createInfo are inconsistent with
1109 * @c numDimensions.
1110 * @exception KTX_INVALID_OPERATION
1111 * @p createInfo is requesting a 3D array or
1112 * 3D cubemap texture.
1113 * @exception KTX_INVALID_OPERATION
1114 * @p createInfo is requesting a cubemap with
1115 * non-square or non-2D images.
1116 * @exception KTX_INVALID_OPERATION
1117 * @p createInfo is requesting more mip levels
1118 * than needed for the specified
1119 * <tt>base{Width,Height,Depth}</tt>.
1120 * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture's images.
1121 */
1122KTX_error_code
1123ktxTexture2_Create(ktxTextureCreateInfo* createInfo,
1124 ktxTextureCreateStorageEnum storageAllocation,
1125 ktxTexture2** newTex)
1126{
1127 KTX_error_code result;
1128
1129 if (newTex == NULL)
1130 return KTX_INVALID_VALUE;
1131
1132 ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
1133 if (tex == NULL)
1134 return KTX_OUT_OF_MEMORY;
1135
1136 result = ktxTexture2_construct(tex, createInfo, storageAllocation);
1137 if (result != KTX_SUCCESS) {
1138 free(tex);
1139 } else {
1140 *newTex = tex;
1141 }
1142 return result;
1143}
1144
1145/**
1146 * @memberof ktxTexture2
1147 * @ingroup writer
1148 * @~English
1149 * @brief Create a ktxTexture2 by making a copy of a ktxTexture2.
1150 *
1151 * The address of the newly created ktxTexture2 is written to the location
1152 * pointed at by @p newTex.
1153 *
1154 * @param[in] orig pointer to the texture to copy.
1155 * @param[in,out] newTex pointer to a location in which store the address of
1156 * the newly created texture.
1157 *
1158 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
1159 *
1160 * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.
1161 */
1162 KTX_error_code
1163 ktxTexture2_CreateCopy(ktxTexture2* orig, ktxTexture2** newTex)
1164 {
1165 KTX_error_code result;
1166
1167 if (newTex == NULL)
1168 return KTX_INVALID_VALUE;
1169
1170 ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
1171 if (tex == NULL)
1172 return KTX_OUT_OF_MEMORY;
1173
1174 result = ktxTexture2_constructCopy(tex, orig);
1175 if (result != KTX_SUCCESS) {
1176 free(tex);
1177 } else {
1178 *newTex = tex;
1179 }
1180 return result;
1181
1182 }
1183
1184/**
1185 * @defgroup reader Reader
1186 * @brief Read KTX-formatted data.
1187 * @{
1188 */
1189
1190/**
1191 * @memberof ktxTexture2
1192 * @~English
1193 * @brief Create a ktxTexture2 from a stdio stream reading from a KTX source.
1194 *
1195 * The address of a newly created ktxTexture2 reflecting the contents of the
1196 * stdio stream is written to the location pointed at by @p newTex.
1197 *
1198 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
1199 * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
1200 * will minimize memory usage by allowing, for example, loading the images
1201 * directly from the source into a Vulkan staging buffer.
1202 *
1203 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
1204 * provided solely to enable implementation of the @e libktx v1 API on top of
1205 * ktxTexture.
1206 *
1207 * @param[in] stdioStream stdio FILE pointer created from the desired file.
1208 * @param[in] createFlags bitmask requesting specific actions during creation.
1209 * @param[in,out] newTex pointer to a location in which store the address of
1210 * the newly created texture.
1211 *
1212 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
1213 *
1214 * @exception KTX_INVALID_VALUE @p newTex is @c NULL.
1215 * @exception KTX_FILE_DATA_ERROR
1216 * Source data is inconsistent with the KTX
1217 * specification.
1218 * @exception KTX_FILE_READ_ERROR
1219 * An error occurred while reading the source.
1220 * @exception KTX_FILE_UNEXPECTED_EOF
1221 * Not enough data in the source.
1222 * @exception KTX_OUT_OF_MEMORY Not enough memory to create the texture object,
1223 * load the images or load the key-value data.
1224 * @exception KTX_UNKNOWN_FILE_FORMAT
1225 * The source is not in KTX format.
1226 * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
1227 * The source describes a texture type not
1228 * supported by OpenGL or Vulkan, e.g, a 3D array.
1229 */
1230KTX_error_code
1231ktxTexture2_CreateFromStdioStream(FILE* stdioStream,
1232 ktxTextureCreateFlags createFlags,
1233 ktxTexture2** newTex)
1234{
1235 KTX_error_code result;
1236 if (newTex == NULL)
1237 return KTX_INVALID_VALUE;
1238
1239 ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
1240 if (tex == NULL)
1241 return KTX_OUT_OF_MEMORY;
1242
1243 result = ktxTexture2_constructFromStdioStream(tex, stdioStream,
1244 createFlags);
1245 if (result == KTX_SUCCESS)
1246 *newTex = (ktxTexture2*)tex;
1247 else {
1248 free(tex);
1249 *newTex = NULL;
1250 }
1251 return result;
1252}
1253
1254/**
1255 * @memberof ktxTexture2
1256 * @~English
1257 * @brief Create a ktxTexture2 from a named KTX file.
1258 *
1259 * The address of a newly created ktxTexture2 reflecting the contents of the
1260 * file is written to the location pointed at by @p newTex.
1261 *
1262 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
1263 * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
1264 * will minimize memory usage by allowing, for example, loading the images
1265 * directly from the source into a Vulkan staging buffer.
1266 *
1267 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
1268 * provided solely to enable implementation of the @e libktx v1 API on top of
1269 * ktxTexture.
1270 *
1271 * @param[in] filename pointer to a char array containing the file name.
1272 * @param[in] createFlags bitmask requesting specific actions during creation.
1273 * @param[in,out] newTex pointer to a location in which store the address of
1274 * the newly created texture.
1275 *
1276 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
1277
1278 * @exception KTX_FILE_OPEN_FAILED The file could not be opened.
1279 * @exception KTX_INVALID_VALUE @p filename is @c NULL.
1280 *
1281 * For other exceptions, see ktxTexture_CreateFromStdioStream().
1282 */
1283KTX_error_code
1284ktxTexture2_CreateFromNamedFile(const char* const filename,
1285 ktxTextureCreateFlags createFlags,
1286 ktxTexture2** newTex)
1287{
1288 KTX_error_code result;
1289
1290 if (newTex == NULL)
1291 return KTX_INVALID_VALUE;
1292
1293 ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
1294 if (tex == NULL)
1295 return KTX_OUT_OF_MEMORY;
1296
1297 result = ktxTexture2_constructFromNamedFile(tex, filename, createFlags);
1298 if (result == KTX_SUCCESS)
1299 *newTex = (ktxTexture2*)tex;
1300 else {
1301 free(tex);
1302 *newTex = NULL;
1303 }
1304 return result;
1305}
1306
1307/**
1308 * @memberof ktxTexture2
1309 * @~English
1310 * @brief Create a ktxTexture2 from KTX-formatted data in memory.
1311 *
1312 * The address of a newly created ktxTexture2 reflecting the contents of the
1313 * serialized KTX data is written to the location pointed at by @p newTex.
1314 *
1315 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
1316 * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
1317 * will minimize memory usage by allowing, for example, loading the images
1318 * directly from the source into a Vulkan staging buffer.
1319 *
1320 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
1321 * provided solely to enable implementation of the @e libktx v1 API on top of
1322 * ktxTexture.
1323 *
1324 * @param[in] bytes pointer to the memory containing the serialized KTX data.
1325 * @param[in] size length of the KTX data in bytes.
1326 * @param[in] createFlags bitmask requesting specific actions during creation.
1327 * @param[in,out] newTex pointer to a location in which store the address of
1328 * the newly created texture.
1329 *
1330 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
1331 *
1332 * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
1333 *
1334 * For other exceptions, see ktxTexture_CreateFromStdioStream().
1335 */
1336KTX_error_code
1337ktxTexture2_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
1338 ktxTextureCreateFlags createFlags,
1339 ktxTexture2** newTex)
1340{
1341 KTX_error_code result;
1342 if (newTex == NULL)
1343 return KTX_INVALID_VALUE;
1344
1345 ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
1346 if (tex == NULL)
1347 return KTX_OUT_OF_MEMORY;
1348
1349 result = ktxTexture2_constructFromMemory(tex, bytes, size,
1350 createFlags);
1351 if (result == KTX_SUCCESS)
1352 *newTex = (ktxTexture2*)tex;
1353 else {
1354 free(tex);
1355 *newTex = NULL;
1356 }
1357 return result;
1358}
1359
1360/**
1361 * @memberof ktxTexture2
1362 * @~English
1363 * @brief Create a ktxTexture2 from KTX-formatted data from a stream.
1364 *
1365 * The address of a newly created ktxTexture2 reflecting the contents of the
1366 * serialized KTX data is written to the location pointed at by @p newTex.
1367 *
1368 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
1369 * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
1370 * will minimize memory usage by allowing, for example, loading the images
1371 * directly from the source into a Vulkan staging buffer.
1372 *
1373 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
1374 * provided solely to enable implementation of the @e libktx v1 API on top of
1375 * ktxTexture.
1376 *
1377 * @param[in] stream pointer to the stream to read KTX data from.
1378 * @param[in] createFlags bitmask requesting specific actions during creation.
1379 * @param[in,out] newTex pointer to a location in which store the address of
1380 * the newly created texture.
1381 *
1382 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
1383 *
1384 * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
1385 *
1386 * For other exceptions, see ktxTexture_CreateFromStdioStream().
1387 */
1388KTX_error_code
1389ktxTexture2_CreateFromStream(ktxStream* stream,
1390 ktxTextureCreateFlags createFlags,
1391 ktxTexture2** newTex)
1392{
1393 KTX_error_code result;
1394 if (newTex == NULL)
1395 return KTX_INVALID_VALUE;
1396
1397 ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
1398 if (tex == NULL)
1399 return KTX_OUT_OF_MEMORY;
1400
1401 result = ktxTexture2_constructFromStream(tex, stream, createFlags);
1402 if (result == KTX_SUCCESS)
1403 *newTex = (ktxTexture2*)tex;
1404 else {
1405 free(tex);
1406 *newTex = NULL;
1407 }
1408 return result;
1409}
1410
1411/**
1412 * @memberof ktxTexture2
1413 * @~English
1414 * @brief Destroy a ktxTexture2 object.
1415 *
1416 * This frees the memory associated with the texture contents and the memory
1417 * of the ktxTexture2 object. This does @e not delete any OpenGL or Vulkan
1418 * texture objects created by ktxTexture2_GLUpload or ktxTexture2_VkUpload.
1419 *
1420 * @param[in] This pointer to the ktxTexture2 object to destroy
1421 */
1422void
1423ktxTexture2_Destroy(ktxTexture2* This)
1424{
1425 ktxTexture2_destruct(This);
1426 free(This);
1427}
1428
1429/**
1430 * @memberof ktxTexture2 @private
1431 * @~English
1432 * @brief Calculate the size of the image data for the specified number
1433 * of levels.
1434 *
1435 * The data size is the sum of the sizes of each level up to the number
1436 * specified and includes any @c mipPadding between levels. It does
1437 * not include initial @c mipPadding required in the file.
1438 *
1439 * @param[in] This pointer to the ktxTexture object of interest.
1440 * @param[in] levels number of levels whose data size to return.
1441 *
1442 * @return the data size in bytes.
1443 */
1444ktx_size_t
1445ktxTexture2_calcDataSizeLevels(ktxTexture2* This, ktx_uint32_t levels)
1446{
1447 ktx_size_t dataSize = 0;
1448
1449 assert(This != NULL);
1450 assert(This->supercompressionScheme == KTX_SS_NONE);
1451 assert(levels <= This->numLevels);
1452 for (ktx_uint32_t i = levels - 1; i > 0; i--) {
1453 ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,
1454 KTX_FORMAT_VERSION_TWO);
1455 dataSize += _KTX_PADN(This->_private->_requiredLevelAlignment,
1456 levelSize);
1457 }
1458 dataSize += ktxTexture_calcLevelSize(ktxTexture(This), 0,
1459 KTX_FORMAT_VERSION_TWO);
1460 return dataSize;
1461}
1462
1463/**
1464 * @memberof ktxTexture2 @private
1465 * @~English
1466 *
1467 * @copydoc ktxTexture::ktxTexture_doCalcFaceLodSize
1468 */
1469ktx_size_t
1470ktxTexture2_calcFaceLodSize(ktxTexture2* This, ktx_uint32_t level)
1471{
1472 assert(This != NULL);
1473 assert(This->supercompressionScheme == KTX_SS_NONE);
1474 /*
1475 * For non-array cubemaps this is the size of a face. For everything
1476 * else it is the size of the level.
1477 */
1478 if (This->isCubemap && !This->isArray)
1479 return ktxTexture_calcImageSize(ktxTexture(This), level,
1480 KTX_FORMAT_VERSION_TWO);
1481 else
1482 return This->_private->_levelIndex[level].uncompressedByteLength;
1483}
1484
1485/**
1486 * @memberof ktxTexture2 @private
1487 * @~English
1488 * @brief Return the offset of a level in bytes from the start of the image
1489 * data in a ktxTexture.
1490 *
1491 * Since the offset is from the start of the image data, it does not include the initial
1492 * @c mipPadding required in the file.
1493 *
1494 * @param[in] This pointer to the ktxTexture object of interest.
1495 * @param[in] level level whose offset to return.
1496 *
1497 * @return the data size in bytes.
1498 */
1499ktx_size_t
1500ktxTexture2_calcLevelOffset(ktxTexture2* This, ktx_uint32_t level)
1501{
1502 assert (This != NULL);
1503 assert(This->supercompressionScheme == KTX_SS_NONE);
1504 assert (level < This->numLevels);
1505 ktx_size_t levelOffset = 0;
1506 for (ktx_uint32_t i = This->numLevels - 1; i > level; i--) {
1507 ktx_size_t levelSize;
1508 levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,
1509 KTX_FORMAT_VERSION_TWO);
1510 levelOffset += _KTX_PADN(This->_private->_requiredLevelAlignment,
1511 levelSize);
1512 }
1513 return levelOffset;
1514}
1515
1516
1517/**
1518 * @memberof ktxTexture2 @private
1519 * @~English
1520 * @brief Retrieve the offset of a level's first image within the KTX2 file.
1521 *
1522 * @param[in] This pointer to the ktxTexture object of interest.
1523 */
1524ktx_uint64_t ktxTexture2_levelFileOffset(ktxTexture2* This, ktx_uint32_t level)
1525{
1526 assert(This->_private->_firstLevelFileOffset != 0);
1527 return This->_private->_levelIndex[level].byteOffset
1528 + This->_private->_firstLevelFileOffset;
1529}
1530
1531// Recursive function to return the greatest common divisor of a and b.
1532static uint32_t
1533gcd(uint32_t a, uint32_t b) {
1534 if (a == 0)
1535 return b;
1536 return gcd(b % a, a);
1537}
1538
1539// Function to return the least common multiple of a & 4.
1540uint32_t
1541lcm4(uint32_t a)
1542{
1543 if (!(a & 0x03))
1544 return a; // a is a multiple of 4.
1545 return (a*4) / gcd(a, 4);
1546}
1547
1548/**
1549 * @memberof ktxTexture2 @private
1550 * @~English
1551 * @brief Return the required alignment for levels of this texture.
1552 *
1553 * @param[in] This pointer to the ktxTexture2 object of interest.
1554 *
1555 * @return The required alignment for levels.
1556 */
1557 ktx_uint32_t
1558 ktxTexture2_calcRequiredLevelAlignment(ktxTexture2* This)
1559 {
1560 ktx_uint32_t alignment;
1561 if (This->supercompressionScheme != KTX_SS_NONE)
1562 alignment = 1;
1563 else
1564 alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);
1565 return alignment;
1566 }
1567
1568/**
1569 * @memberof ktxTexture2 @private
1570 * @~English
1571 * @brief Return what the required alignment for levels of this texture will be after inflation.
1572 *
1573 * @param[in] This pointer to the ktxTexture2 object of interest.
1574 *
1575 * @return The required alignment for levels.
1576 */
1577ktx_uint32_t
1578ktxTexture2_calcPostInflationLevelAlignment(ktxTexture2* This)
1579{
1580 ktx_uint32_t alignment;
1581
1582 // Should actually work for none supercompressed but don't want to
1583 // encourage use of it.
1584 assert(This->supercompressionScheme >= KTX_SS_ZSTD);
1585
1586 if (This->vkFormat != VK_FORMAT_UNDEFINED)
1587 alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);
1588 else
1589 alignment = 16;
1590
1591 return alignment;
1592}
1593
1594
1595/**
1596 * @memberof ktxTexture2
1597 * @~English
1598 * @brief Return information about the components of an image in a texture.
1599 *
1600 * @param[in] This pointer to the ktxTexture object of interest.
1601 * @param[in,out] pNumComponents pointer to location in which to write the
1602 * number of components in the textures images.
1603 * @param[in,out] pComponentByteLength
1604 * pointer to the location in which to write
1605 * byte length of a component.
1606 */
1607void
1608ktxTexture2_GetComponentInfo(ktxTexture2* This, uint32_t* pNumComponents,
1609 uint32_t* pComponentByteLength)
1610{
1611 // FIXME Need to handle packed case.
1612 getDFDComponentInfoUnpacked(This->pDfd, pNumComponents,
1613 pComponentByteLength);
1614}
1615
1616/**
1617 * @memberof ktxTexture2
1618 * @~English
1619 * @brief Return the number of components in an image of the texture.
1620 *
1621 * Returns the number of components indicated by the DFD's sample information
1622 * in accordance with the color model. For uncompressed formats it will be the actual
1623 * number of components in the image. For block-compressed formats, it will be 1 or 2
1624 * according to the format's DFD color model. For Basis compressed textures, the
1625 * function examines the ids of the channels indicated by the DFD and uses that
1626 * information to determine and return the number of components in the image
1627 * @e before encoding and deflation so it can be used to help choose a suitable
1628 * transcode target format.
1629 *
1630 * @param[in] This pointer to the ktxTexture object of interest.
1631 *
1632 * @return the number of components.
1633 */
1634ktx_uint32_t
1635ktxTexture2_GetNumComponents(ktxTexture2* This)
1636{
1637 uint32_t* pBdb = This->pDfd + 1;
1638 uint32_t dfdNumComponents = getDFDNumComponents(This->pDfd);
1639 uint32_t colorModel = KHR_DFDVAL(pBdb, MODEL);
1640 if (colorModel < KHR_DF_MODEL_DXT1A) {
1641 return dfdNumComponents;
1642 } else {
1643 switch (colorModel) {
1644 case KHR_DF_MODEL_ETC1S:
1645 {
1646 uint32_t channel0Id = KHR_DFDSVAL(pBdb, 0, CHANNELID);
1647 if (dfdNumComponents == 1) {
1648 if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB)
1649 return 3;
1650 else
1651 return 1;
1652 } else {
1653 uint32_t channel1Id = KHR_DFDSVAL(pBdb, 1, CHANNELID);
1654 if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB
1655 && channel1Id == KHR_DF_CHANNEL_ETC1S_AAA)
1656 return 4;
1657 else {
1658 // An invalid combination of channel Ids should never
1659 // have been set during creation or should have been
1660 // caught when the file was loaded.
1661 assert(channel0Id == KHR_DF_CHANNEL_ETC1S_RRR
1662 && channel1Id == KHR_DF_CHANNEL_ETC1S_GGG);
1663 return 2;
1664 }
1665 }
1666 break;
1667 }
1668 case KHR_DF_MODEL_UASTC:
1669 switch (KHR_DFDSVAL(pBdb, 0, CHANNELID)) {
1670 case KHR_DF_CHANNEL_UASTC_RRR:
1671 return 1;
1672 case KHR_DF_CHANNEL_UASTC_RRRG:
1673 return 2;
1674 case KHR_DF_CHANNEL_UASTC_RGB:
1675 return 3;
1676 case KHR_DF_CHANNEL_UASTC_RGBA:
1677 return 4;
1678 default:
1679 // Same comment as for the assert in the ETC1 case.
1680 assert(false);
1681 return 1;
1682 }
1683 break;
1684 default:
1685 return dfdNumComponents;
1686 }
1687 }
1688}
1689
1690/**
1691 * @memberof ktxTexture2
1692 * @~English
1693 * @brief Find the offset of an image within a ktxTexture's image data.
1694 *
1695 * As there is no such thing as a 3D cubemap we make the 3rd location parameter
1696 * do double duty. Only works for non-supercompressed textures as
1697 * there is no way to tell where an image is for a supercompressed one.
1698 *
1699 * @param[in] This pointer to the ktxTexture object of interest.
1700 * @param[in] level mip level of the image.
1701 * @param[in] layer array layer of the image.
1702 * @param[in] faceSlice cube map face or depth slice of the image.
1703 * @param[in,out] pOffset pointer to location to store the offset.
1704 *
1705 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
1706 *
1707 * @exception KTX_INVALID_OPERATION
1708 * @p level, @p layer or @p faceSlice exceed the
1709 * dimensions of the texture.
1710 * @exception KTX_INVALID_OPERATION Texture is supercompressed.
1711 * @exception KTX_INVALID_VALID @p This is NULL.
1712 */
1713KTX_error_code
1714ktxTexture2_GetImageOffset(ktxTexture2* This, ktx_uint32_t level,
1715 ktx_uint32_t layer, ktx_uint32_t faceSlice,
1716 ktx_size_t* pOffset)
1717{
1718 if (This == NULL)
1719 return KTX_INVALID_VALUE;
1720
1721 if (level >= This->numLevels || layer >= This->numLayers)
1722 return KTX_INVALID_OPERATION;
1723
1724 if (This->supercompressionScheme != KTX_SS_NONE)
1725 return KTX_INVALID_OPERATION;
1726
1727 if (This->isCubemap) {
1728 if (faceSlice >= This->numFaces)
1729 return KTX_INVALID_OPERATION;
1730 } else {
1731 ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level);
1732 if (faceSlice >= maxSlice)
1733 return KTX_INVALID_OPERATION;
1734 }
1735
1736 // Get the offset of the start of the level.
1737 *pOffset = ktxTexture2_levelDataOffset(This, level);
1738
1739 // All layers, faces & slices within a level are the same size.
1740 if (layer != 0) {
1741 ktx_size_t layerSize;
1742 layerSize = ktxTexture_layerSize(ktxTexture(This), level,
1743 KTX_FORMAT_VERSION_TWO);
1744 *pOffset += layer * layerSize;
1745 }
1746 if (faceSlice != 0) {
1747 ktx_size_t imageSize;
1748 imageSize = ktxTexture2_GetImageSize(This, level);
1749 *pOffset += faceSlice * imageSize;
1750 }
1751 return KTX_SUCCESS;
1752}
1753
1754/**
1755 * @memberof ktxTexture2
1756 * @~English
1757 * @brief Retrieve the opto-electrical transfer function of the images.
1758 *
1759 * @param[in] This pointer to the ktxTexture2 object of interest.
1760 *
1761 * @return A @c khr_df_transfer enum value specifying the OETF.
1762 */
1763khr_df_transfer_e
1764ktxTexture2_GetOETF_e(ktxTexture2* This)
1765{
1766 return KHR_DFDVAL(This->pDfd+1, TRANSFER);
1767}
1768
1769/**
1770 * @memberof ktxTexture2
1771 * @~English
1772 * @brief Retrieve the opto-electrical transfer function of the images.
1773 * @deprecated Retained for backward compatibility. Use ktxTexture2\_GetOETF\_e()
1774 *
1775 * @param[in] This pointer to the ktxTexture2 object of interest.
1776 *
1777 * @return A @c khr_df_transfer enum value specifying the OETF, returned as
1778 * @c ktx_uint32_t.
1779 */
1780ktx_uint32_t
1781ktxTexture2_GetOETF(ktxTexture2* This)
1782{
1783 return KHR_DFDVAL(This->pDfd+1, TRANSFER);
1784}
1785
1786/**
1787 * @memberof ktxTexture2
1788 * @~English
1789 * @brief Retrieve the DFD color model of the images.
1790 *
1791 * @param[in] This pointer to the ktxTexture2 object of interest.
1792 *
1793 * @return A @c khr_df_transfer enum value specifying the color model.
1794 */
1795khr_df_model_e
1796ktxTexture2_GetColorModel_e(ktxTexture2* This)
1797{
1798 return KHR_DFDVAL(This->pDfd+1, MODEL);
1799}
1800
1801/**
1802 * @memberof ktxTexture2
1803 * @~English
1804 * @brief Retrieve whether the RGB components have been premultiplied by the alpha component.
1805 *
1806 * @param[in] This pointer to the ktxTexture2 object of interest.
1807 *
1808 * @return KTX\_TRUE if the components are premultiplied, KTX_FALSE otherwise.
1809 */
1810ktx_bool_t
1811ktxTexture2_GetPremultipliedAlpha(ktxTexture2* This)
1812{
1813 return KHR_DFDVAL(This->pDfd+1, FLAGS) & KHR_DF_FLAG_ALPHA_PREMULTIPLIED;
1814}
1815
1816/**
1817 * @memberof ktxTexture2
1818 * @~English
1819 * @brief Query if the images are in a transcodable format.
1820 *
1821 * @param[in] This pointer to the ktxTexture2 object of interest.
1822 */
1823ktx_bool_t
1824ktxTexture2_NeedsTranscoding(ktxTexture2* This)
1825{
1826 if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_ETC1S)
1827 return true;
1828 else if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_UASTC)
1829 return true;
1830 else
1831 return false;
1832}
1833
1834/**
1835 * @memberof ktxTexture2
1836 * @~English
1837 * @brief Return the total size in bytes of the uncompressed data of a ktxTexture2.
1838 *
1839 * If supercompressionScheme == KTX_SS_NONE or
1840 * KTX_SS_BASIS_LZ, returns the value of @c This->dataSize
1841 * else if supercompressionScheme == KTX_SS_ZSTD, it returns the
1842 * sum of the uncompressed sizes of each mip level plus space for the level padding. With no
1843 * supercompression the data size and uncompressed data size are the same. For Basis
1844 * supercompression the uncompressed size cannot be known until the data is transcoded
1845 * so the compressed size is returned.
1846 *
1847 * @param[in] This pointer to the ktxTexture1 object of interest.
1848 */
1849ktx_size_t
1850ktxTexture2_GetDataSizeUncompressed(ktxTexture2* This)
1851{
1852 switch (This->supercompressionScheme) {
1853 case KTX_SS_BASIS_LZ:
1854 case KTX_SS_NONE:
1855 return This->dataSize;
1856 case KTX_SS_ZSTD:
1857 {
1858 ktx_size_t uncompressedSize = 0;
1859 ktx_uint32_t uncompressedLevelAlignment;
1860 ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;
1861
1862 uncompressedLevelAlignment =
1863 ktxTexture2_calcPostInflationLevelAlignment(This);
1864
1865 for (ktx_int32_t level = This->numLevels - 1; level >= 1; level--) {
1866 ktx_size_t uncompressedLevelSize;
1867 uncompressedLevelSize = levelIndex[level].uncompressedByteLength;
1868 uncompressedLevelSize = _KTX_PADN(uncompressedLevelAlignment,
1869 uncompressedLevelSize);
1870 uncompressedSize += uncompressedLevelSize;
1871 }
1872 uncompressedSize += levelIndex[0].uncompressedByteLength;
1873 return uncompressedSize;
1874 }
1875 case KTX_SS_BEGIN_VENDOR_RANGE:
1876 case KTX_SS_END_VENDOR_RANGE:
1877 case KTX_SS_BEGIN_RESERVED:
1878 default:
1879 return 0;
1880 }
1881}
1882
1883/**
1884 * @memberof ktxTexture2
1885 * @~English
1886 * @brief Calculate & return the size in bytes of an image at the specified
1887 * mip level.
1888 *
1889 * For arrays, this is the size of a layer, for cubemaps, the size of a face
1890 * and for 3D textures, the size of a depth slice.
1891 *
1892 * The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.
1893 *
1894 * @param[in] This pointer to the ktxTexture2 object of interest.
1895 * @param[in] level level of interest. *
1896 */
1897ktx_size_t
1898ktxTexture2_GetImageSize(ktxTexture2* This, ktx_uint32_t level)
1899{
1900 return ktxTexture_calcImageSize(ktxTexture(This), level,
1901 KTX_FORMAT_VERSION_TWO);
1902}
1903
1904/**
1905 * @memberof ktxTexture2
1906 * @~English
1907 * @brief Iterate over the mip levels in a ktxTexture2 object.
1908 *
1909 * This is almost identical to ktxTexture_IterateLevelFaces(). The difference is
1910 * that the blocks of image data for non-array cube maps include all faces of
1911 * a mip level.
1912 *
1913 * This function works even if @p This->pData == 0 so it can be used to
1914 * obtain offsets and sizes for each level by callers who have loaded the data
1915 * externally.
1916 *
1917 * Intended for use only when supercompressionScheme == SUPERCOMPRESSION_NONE.
1918 *
1919 * @param[in] This handle of the ktxTexture opened on the data.
1920 * @param[in,out] iterCb the address of a callback function which is called
1921 * with the data for each image block.
1922 * @param[in,out] userdata the address of application-specific data which is
1923 * passed to the callback along with the image data.
1924 *
1925 * @return KTX_SUCCESS on success, other KTX_* enum values on error. The
1926 * following are returned directly by this function. @p iterCb may
1927 * return these for other causes or may return additional errors.
1928 *
1929 * @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not
1930 * decreasing
1931 * @exception KTX_INVALID_OPERATION supercompressionScheme != SUPERCOMPRESSION_NONE.
1932 * @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.
1933 *
1934 */
1935KTX_error_code
1936ktxTexture2_IterateLevels(ktxTexture2* This, PFNKTXITERCB iterCb, void* userdata)
1937{
1938 KTX_error_code result = KTX_SUCCESS;
1939 //ZSTD_DCtx* dctx;
1940 //ktx_uint8_t* decompBuf;
1941 ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;
1942
1943 if (This == NULL)
1944 return KTX_INVALID_VALUE;
1945
1946 if (iterCb == NULL)
1947 return KTX_INVALID_VALUE;
1948
1949 if (This->supercompressionScheme != KTX_SS_NONE)
1950 return KTX_INVALID_OPERATION;
1951
1952 for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)
1953 {
1954 ktx_uint32_t width, height, depth;
1955 ktx_uint64_t levelSize;
1956 ktx_uint64_t offset;
1957
1958 /* Array textures have the same number of layers at each mip level. */
1959 width = MAX(1, This->baseWidth >> level);
1960 height = MAX(1, This->baseHeight >> level);
1961 depth = MAX(1, This->baseDepth >> level);
1962
1963 levelSize = levelIndex[level].uncompressedByteLength;
1964 offset = ktxTexture2_levelDataOffset(This, level);
1965
1966 /* All array layers are passed in a group because that is how
1967 * GL & Vulkan need them. Hence no
1968 * for (layer = 0; layer < This->numLayers)
1969 */
1970 result = iterCb(level, 0, width, height, depth,
1971 levelSize, This->pData + offset, userdata);
1972 if (result != KTX_SUCCESS)
1973 break;
1974 }
1975
1976 return result;
1977}
1978
1979/**
1980 * @memberof ktxTexture2
1981 * @~English
1982 * @brief Iterate over the images in a ktxTexture2 object while loading the
1983 * image data.
1984 *
1985 * This operates similarly to ktxTexture_IterateLevelFaces() except that it
1986 * loads the images from the ktxTexture2's source to a temporary buffer
1987 * while iterating. If supercompressionScheme == KTX_SS_ZSTD,
1988 * it will inflate the data before passing it to the callback. The callback function
1989 * must copy the image data if it wishes to preserve it as the temporary buffer
1990 * is reused for each level and is freed when this function exits.
1991 *
1992 * This function is helpful for reducing memory usage when uploading the data
1993 * to a graphics API.
1994 *
1995 * Intended for use only when supercompressionScheme == SUPERCOMPRESSION_NONE
1996 * or SUPERCOMPRESSION_ZSTD. As there is no access to the ktxTexture's data on
1997 * conclusion of this function, destroying the texture on completion is recommended.
1998 *
1999 * @param[in] This pointer to the ktxTexture2 object of interest.
2000 * @param[in,out] iterCb the address of a callback function which is called
2001 * with the data for each image.
2002 * @param[in,out] userdata the address of application-specific data which is
2003 * passed to the callback along with the image data.
2004 *
2005 * @return KTX_SUCCESS on success, other KTX_* enum values on error. The
2006 * following are returned directly by this function. @p iterCb may
2007 * return these for other causes or may return additional errors.
2008 *
2009 * @exception KTX_FILE_DATA_ERROR mip level sizes are increasing not
2010 * decreasing
2011 * @exception KTX_INVALID_OPERATION the ktxTexture2 was not created from a
2012 * stream, i.e there is no data to load, or
2013 * this ktxTexture2's images have already
2014 * been loaded.
2015 * @exception KTX_INVALID_OPERATION
2016 * supercompressionScheme != SUPERCOMPRESSION_NONE.
2017 * and supercompressionScheme != SUPERCOMPRESSION_ZSTD.
2018 * @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.
2019 * @exception KTX_OUT_OF_MEMORY not enough memory to allocate a block to
2020 * hold the base level image.
2021 */
2022KTX_error_code
2023ktxTexture2_IterateLoadLevelFaces(ktxTexture2* This, PFNKTXITERCB iterCb,
2024 void* userdata)
2025{
2026 DECLARE_PROTECTED(ktxTexture);
2027 ktxStream* stream = (ktxStream *)&prtctd->_stream;
2028 ktxLevelIndexEntry* levelIndex;
2029 ktx_size_t dataSize = 0, uncompressedDataSize = 0;
2030 KTX_error_code result = KTX_SUCCESS;
2031 ktx_uint8_t* dataBuf = NULL;
2032 ktx_uint8_t* uncompressedDataBuf = NULL;
2033 ktx_uint8_t* pData;
2034 ZSTD_DCtx* dctx = NULL;
2035
2036 if (This == NULL)
2037 return KTX_INVALID_VALUE;
2038
2039 if (This->classId != ktxTexture2_c)
2040 return KTX_INVALID_OPERATION;
2041
2042 if (This->supercompressionScheme != KTX_SS_NONE &&
2043 This->supercompressionScheme != KTX_SS_ZSTD)
2044 return KTX_INVALID_OPERATION;
2045
2046 if (iterCb == NULL)
2047 return KTX_INVALID_VALUE;
2048
2049 if (prtctd->_stream.data.file == NULL)
2050 // This Texture not created from a stream or images are already loaded.
2051 return KTX_INVALID_OPERATION;
2052
2053 levelIndex = This->_private->_levelIndex;
2054
2055 // Allocate memory sufficient for the base level
2056 dataSize = levelIndex[0].byteLength;
2057 dataBuf = malloc(dataSize);
2058 if (!dataBuf)
2059 return KTX_OUT_OF_MEMORY;
2060 if (This->supercompressionScheme == KTX_SS_ZSTD) {
2061 uncompressedDataSize = levelIndex[0].uncompressedByteLength;
2062 uncompressedDataBuf = malloc(uncompressedDataSize);
2063 if (!uncompressedDataBuf) {
2064 result = KTX_OUT_OF_MEMORY;
2065 goto cleanup;
2066 }
2067 dctx = ZSTD_createDCtx();
2068 pData = uncompressedDataBuf;
2069 } else {
2070 pData = dataBuf;
2071 }
2072
2073 for (ktx_int32_t level = This->numLevels - 1; level >= 0; --level)
2074 {
2075 ktx_size_t levelSize;
2076 GLsizei width, height, depth;
2077
2078 // Array textures have the same number of layers at each mip level.
2079 width = MAX(1, This->baseWidth >> level);
2080 height = MAX(1, This->baseHeight >> level);
2081 depth = MAX(1, This->baseDepth >> level);
2082
2083 levelSize = levelIndex[level].byteLength;
2084 if (dataSize < levelSize) {
2085 // Levels cannot be larger than the base level
2086 result = KTX_FILE_DATA_ERROR;
2087 goto cleanup;
2088 }
2089
2090 // Use setpos so we skip any padding.
2091 result = stream->setpos(stream,
2092 ktxTexture2_levelFileOffset(This, level));
2093 if (result != KTX_SUCCESS)
2094 goto cleanup;
2095
2096 result = stream->read(stream, dataBuf, levelSize);
2097 if (result != KTX_SUCCESS)
2098 goto cleanup;
2099
2100 if (This->supercompressionScheme == KTX_SS_ZSTD) {
2101 levelSize =
2102 ZSTD_decompressDCtx(dctx, uncompressedDataBuf,
2103 uncompressedDataSize,
2104 dataBuf,
2105 levelSize);
2106 if (ZSTD_isError(levelSize)) {
2107 ZSTD_ErrorCode error = ZSTD_getErrorCode(levelSize);
2108 switch(error) {
2109 case ZSTD_error_dstSize_tooSmall:
2110 return KTX_INVALID_VALUE; // inflatedDataCapacity too small.
2111 case ZSTD_error_memory_allocation:
2112 return KTX_OUT_OF_MEMORY;
2113 default:
2114 return KTX_FILE_DATA_ERROR;
2115 }
2116 }
2117 // We don't fix up the texture's dataSize, levelIndex or
2118 // _requiredAlignment because after this function completes there
2119 // is no way to get at the texture's data.
2120 //nindex[level].byteOffset = levelOffset;
2121 //nindex[level].uncompressedByteLength = nindex[level].byteLength =
2122 //levelByteLength;
2123 }
2124
2125#if IS_BIG_ENDIAN
2126 switch (prtctd->_typeSize) {
2127 case 2:
2128 _ktxSwapEndian16((ktx_uint16_t*)pData, levelSize / 2);
2129 break;
2130 case 4:
2131 _ktxSwapEndian32((ktx_uint32_t*)pDest, levelSize / 4);
2132 break;
2133 case 8:
2134 _ktxSwapEndian64((ktx_uint64_t*)pDest, levelSize / 8);
2135 break;
2136 }
2137#endif
2138
2139 // With the exception of non-array cubemaps the entire level
2140 // is passed at once because that is how OpenGL and Vulkan need them.
2141 // Vulkan could take all the faces at once too but we iterate
2142 // them separately for OpenGL.
2143 if (This->isCubemap && !This->isArray) {
2144 ktx_uint8_t* pFace = pData;
2145 struct blockCount {
2146 ktx_uint32_t x, y;
2147 } blockCount;
2148 ktx_size_t faceSize;
2149
2150 blockCount.x
2151 = (uint32_t)ceilf((float)width / prtctd->_formatSize.blockWidth);
2152 blockCount.y
2153 = (uint32_t)ceilf((float)height / prtctd->_formatSize.blockHeight);
2154 blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x);
2155 blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y);
2156 faceSize = blockCount.x * blockCount.y
2157 * prtctd->_formatSize.blockSizeInBits / 8;
2158
2159 for (ktx_uint32_t face = 0; face < This->numFaces; ++face) {
2160 result = iterCb(level, face,
2161 width, height, depth,
2162 (ktx_uint32_t)faceSize, pFace, userdata);
2163 pFace += faceSize;
2164 if (result != KTX_SUCCESS)
2165 goto cleanup;
2166 }
2167 } else {
2168 result = iterCb(level, 0,
2169 width, height, depth,
2170 (ktx_uint32_t)levelSize, pData, userdata);
2171 if (result != KTX_SUCCESS)
2172 goto cleanup;
2173 }
2174 }
2175
2176 // No further need for this.
2177 stream->destruct(stream);
2178 This->_private->_firstLevelFileOffset = 0;
2179cleanup:
2180 free(dataBuf);
2181 if (uncompressedDataBuf) free(uncompressedDataBuf);
2182 if (dctx) ZSTD_freeDCtx(dctx);
2183
2184 return result;
2185}
2186
2187KTX_error_code
2188ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
2189 ktx_uint8_t* pInflatedData,
2190 ktx_size_t inflatedDataCapacity);
2191/**
2192 * @memberof ktxTexture2
2193 * @~English
2194 * @brief Load all the image data from the ktxTexture2's source.
2195 *
2196 * The data will be inflated if supercompressionScheme == SUPERCOMPRESSION_ZSTD.
2197 * The data is loaded into the provided buffer or to an internally allocated
2198 * buffer, if @p pBuffer is @c NULL. Callers providing their own buffer must
2199 * ensure the buffer large enough to hold the inflated data for files deflated
2200 * with Zstd. See ktxTexture2_GetDataSizeUncompressed().
2201 *
2202 * The texture's levelIndex, dataSize, DFD and supercompressionScheme will
2203 * all be updated after successful inflation to reflect the inflated data.
2204 *
2205 * @param[in] This pointer to the ktxTexture object of interest.
2206 * @param[in] pBuffer pointer to the buffer in which to load the image data.
2207 * @param[in] bufSize size of the buffer pointed at by @p pBuffer.
2208 *
2209 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
2210 *
2211 * @exception KTX_INVALID_VALUE @p This is NULL.
2212 * @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.
2213 * @exception KTX_INVALID_OPERATION
2214 * The data has already been loaded or the
2215 * ktxTexture was not created from a KTX source.
2216 * @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.
2217 */
2218KTX_error_code
2219ktxTexture2_LoadImageData(ktxTexture2* This,
2220 ktx_uint8_t* pBuffer, ktx_size_t bufSize)
2221{
2222 DECLARE_PROTECTED(ktxTexture);
2223 DECLARE_PRIVATE(ktxTexture2);
2224 ktx_uint8_t* pDest;
2225 ktx_uint8_t* pDeflatedData = 0;
2226 ktx_uint8_t* pReadBuf;
2227 KTX_error_code result = KTX_SUCCESS;
2228 ktx_size_t inflatedDataCapacity = ktxTexture2_GetDataSizeUncompressed(This);
2229
2230 if (This == NULL)
2231 return KTX_INVALID_VALUE;
2232
2233 if (This->pData != NULL)
2234 return KTX_INVALID_OPERATION; // Data already loaded.
2235
2236 if (prtctd->_stream.data.file == NULL)
2237 // This Texture not created from a stream or images already loaded;
2238 return KTX_INVALID_OPERATION;
2239
2240 if (pBuffer == NULL) {
2241 This->pData = malloc(inflatedDataCapacity);
2242 if (This->pData == NULL)
2243 return KTX_OUT_OF_MEMORY;
2244 pDest = This->pData;
2245 } else if (bufSize < inflatedDataCapacity) {
2246 return KTX_INVALID_VALUE;
2247 } else {
2248 pDest = pBuffer;
2249 }
2250
2251 if (This->supercompressionScheme == KTX_SS_ZSTD) {
2252 // Create buffer to hold deflated data.
2253 pDeflatedData = malloc(This->dataSize);
2254 if (pDeflatedData == NULL)
2255 return KTX_OUT_OF_MEMORY;
2256 pReadBuf = pDeflatedData;
2257 } else {
2258 pReadBuf = pDest;
2259 }
2260
2261 // Seek to data for first level as there may be padding between the
2262 // metadata/sgd and the image data.
2263
2264 result = prtctd->_stream.setpos(&prtctd->_stream,
2265 private->_firstLevelFileOffset);
2266 if (result != KTX_SUCCESS)
2267 return result;
2268
2269 result = prtctd->_stream.read(&prtctd->_stream, pReadBuf,
2270 This->dataSize);
2271 if (result != KTX_SUCCESS)
2272 return result;
2273
2274 if (This->supercompressionScheme == KTX_SS_ZSTD) {
2275 assert(pDeflatedData != NULL);
2276 result = ktxTexture2_inflateZstdInt(This, pDeflatedData, pDest,
2277 inflatedDataCapacity);
2278 free(pDeflatedData);
2279 if (result != KTX_SUCCESS) {
2280 if (pBuffer == NULL) {
2281 free(This->pData);
2282 This->pData = 0;
2283 }
2284 return result;
2285 }
2286 }
2287
2288 if (IS_BIG_ENDIAN) {
2289 // Perform endianness conversion on texture data.
2290 // To avoid mip padding, need to convert each level individually.
2291 for (ktx_uint32_t level = 0; level < This->numLevels; ++level)
2292 {
2293 ktx_size_t levelOffset;
2294 ktx_size_t levelByteLength;
2295
2296 levelByteLength = private->_levelIndex[level].byteLength;
2297 levelOffset = ktxTexture2_levelDataOffset(This, level);
2298 pDest = This->pData + levelOffset;
2299 switch (prtctd->_typeSize) {
2300 case 2:
2301 _ktxSwapEndian16((ktx_uint16_t*)pDest, levelByteLength / 2);
2302 break;
2303 case 4:
2304 _ktxSwapEndian32((ktx_uint32_t*)pDest, levelByteLength / 4);
2305 break;
2306 case 8:
2307 _ktxSwapEndian64((ktx_uint64_t*)pDest, levelByteLength / 8);
2308 break;
2309 }
2310 }
2311 }
2312
2313 // No further need for stream or file offset.
2314 prtctd->_stream.destruct(&prtctd->_stream);
2315 private->_firstLevelFileOffset = 0;
2316 return result;
2317}
2318
2319/**
2320 * @memberof ktxTexture2 @private
2321 * @~English
2322 * @brief Retrieve the offset of a level's first image within the ktxTexture2's
2323 * image data.
2324 *
2325 * @param[in] This pointer to the ktxTexture2 object of interest.
2326 */
2327ktx_uint64_t ktxTexture2_levelDataOffset(ktxTexture2* This, ktx_uint32_t level)
2328{
2329 return This->_private->_levelIndex[level].byteOffset;
2330}
2331
2332/**
2333 * @memberof ktxTexture2 @private
2334 * @~English
2335 * @brief Inflate the data in a ktxTexture2 object using Zstandard.
2336 *
2337 * The texture's levelIndex, dataSize, DFD and supercompressionScheme will
2338 * all be updated after successful inflation to reflect the inflated data.
2339 *
2340 * @param[in] This pointer to the ktxTexture2 object of interest.
2341 * @param[in] pDeflatedData pointer to a buffer containing the deflated data
2342 * of the entire texture.
2343 * @param[in,out] pInflatedData pointer to a buffer in which to write the inflated
2344 * data.
2345 * @param[in] inflatedDataCapacity capacity of the buffer pointed at by
2346 * @p pInflatedData.
2347 */
2348KTX_error_code
2349ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
2350 ktx_uint8_t* pInflatedData,
2351 ktx_size_t inflatedDataCapacity)
2352{
2353 DECLARE_PROTECTED(ktxTexture);
2354 ktx_uint32_t levelIndexByteLength =
2355 This->numLevels * sizeof(ktxLevelIndexEntry);
2356 uint64_t levelOffset = 0;
2357 ktxLevelIndexEntry* cindex = This->_private->_levelIndex;
2358 ktxLevelIndexEntry* nindex;
2359 ktx_uint32_t uncompressedLevelAlignment;
2360
2361 ZSTD_DCtx* dctx;
2362
2363 if (pDeflatedData == NULL)
2364 return KTX_INVALID_VALUE;
2365
2366 if (pInflatedData == NULL)
2367 return KTX_INVALID_VALUE;
2368
2369 if (This->supercompressionScheme != KTX_SS_ZSTD)
2370 return KTX_INVALID_OPERATION;
2371
2372 nindex = malloc(levelIndexByteLength);
2373 if (nindex == NULL)
2374 return KTX_OUT_OF_MEMORY;
2375
2376 uncompressedLevelAlignment =
2377 ktxTexture2_calcPostInflationLevelAlignment(This);
2378
2379 ktx_size_t inflatedByteLength = 0;
2380 dctx = ZSTD_createDCtx();
2381 for (int32_t level = This->numLevels - 1; level >= 0; level--) {
2382 size_t levelByteLength =
2383 ZSTD_decompressDCtx(dctx, pInflatedData + levelOffset,
2384 inflatedDataCapacity,
2385 &pDeflatedData[cindex[level].byteOffset],
2386 cindex[level].byteLength);
2387 if (ZSTD_isError(levelByteLength)) {
2388 ZSTD_ErrorCode error = ZSTD_getErrorCode(levelByteLength);
2389 switch(error) {
2390 case ZSTD_error_dstSize_tooSmall:
2391 return KTX_INVALID_VALUE; // inflatedDataCapacity too small.
2392 case ZSTD_error_memory_allocation:
2393 return KTX_OUT_OF_MEMORY;
2394 default:
2395 return KTX_FILE_DATA_ERROR;
2396 }
2397 }
2398 nindex[level].byteOffset = levelOffset;
2399 nindex[level].uncompressedByteLength = nindex[level].byteLength =
2400 levelByteLength;
2401 ktx_size_t paddedLevelByteLength
2402 = _KTX_PADN(uncompressedLevelAlignment, levelByteLength);
2403 inflatedByteLength += paddedLevelByteLength;
2404 levelOffset += paddedLevelByteLength;
2405 inflatedDataCapacity -= paddedLevelByteLength;
2406 }
2407 ZSTD_freeDCtx(dctx);
2408
2409 // Now modify the texture.
2410
2411 This->dataSize = inflatedByteLength;
2412 This->supercompressionScheme = KTX_SS_NONE;
2413 memcpy(cindex, nindex, levelIndexByteLength); // Update level index
2414 free(nindex);
2415 This->_private->_requiredLevelAlignment = uncompressedLevelAlignment;
2416 // Set bytesPlane as we're now sized.
2417 uint32_t* bdb = This->pDfd + 1;
2418 // blockSizeInBits was set to the inflated size on file load.
2419 bdb[KHR_DF_WORD_BYTESPLANE0] = prtctd->_formatSize.blockSizeInBits / 8;
2420
2421 return KTX_SUCCESS;
2422}
2423
2424#if !KTX_FEATURE_WRITE
2425
2426/*
2427 * Stubs for writer functions that return a proper error code
2428 */
2429
2430KTX_error_code
2431ktxTexture2_SetImageFromMemory(ktxTexture2* This, ktx_uint32_t level,
2432 ktx_uint32_t layer, ktx_uint32_t faceSlice,
2433 const ktx_uint8_t* src, ktx_size_t srcSize)
2434{
2435 UNUSED(This);
2436 UNUSED(level);
2437 UNUSED(layer);
2438 UNUSED(faceSlice);
2439 UNUSED(src);
2440 UNUSED(srcSize);
2441 return KTX_INVALID_OPERATION;
2442}
2443
2444KTX_error_code
2445ktxTexture2_SetImageFromStdioStream(ktxTexture2* This, ktx_uint32_t level,
2446 ktx_uint32_t layer, ktx_uint32_t faceSlice,
2447 FILE* src, ktx_size_t srcSize)
2448{
2449 UNUSED(This);
2450 UNUSED(level);
2451 UNUSED(layer);
2452 UNUSED(faceSlice);
2453 UNUSED(src);
2454 UNUSED(srcSize);
2455 return KTX_INVALID_OPERATION;
2456}
2457
2458KTX_error_code
2459ktxTexture2_WriteToStdioStream(ktxTexture2* This, FILE* dstsstr)
2460{
2461 UNUSED(This);
2462 UNUSED(dstsstr);
2463 return KTX_INVALID_OPERATION;
2464}
2465
2466KTX_error_code
2467ktxTexture2_WriteToNamedFile(ktxTexture2* This, const char* const dstname)
2468{
2469 UNUSED(This);
2470 UNUSED(dstname);
2471 return KTX_INVALID_OPERATION;
2472}
2473
2474KTX_error_code
2475ktxTexture2_WriteToMemory(ktxTexture2* This,
2476 ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)
2477{
2478 UNUSED(This);
2479 UNUSED(ppDstBytes);
2480 UNUSED(pSize);
2481 return KTX_INVALID_OPERATION;
2482}
2483
2484KTX_error_code
2485ktxTexture2_WriteToStream(ktxTexture2* This,
2486 ktxStream* dststr)
2487{
2488 UNUSED(This);
2489 UNUSED(dststr);
2490 return KTX_INVALID_OPERATION;
2491}
2492
2493#endif
2494
2495/*
2496 * Initialized here at the end to avoid the need for multiple declarations of
2497 * the virtual functions.
2498 */
2499
2500struct ktxTexture_vtblInt ktxTexture2_vtblInt = {
2501 (PFNCALCDATASIZELEVELS)ktxTexture2_calcDataSizeLevels,
2502 (PFNCALCFACELODSIZE)ktxTexture2_calcFaceLodSize,
2503 (PFNCALCLEVELOFFSET)ktxTexture2_calcLevelOffset
2504};
2505
2506struct ktxTexture_vtbl ktxTexture2_vtbl = {
2507 (PFNKTEXDESTROY)ktxTexture2_Destroy,
2508 (PFNKTEXGETIMAGEOFFSET)ktxTexture2_GetImageOffset,
2509 (PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture2_GetDataSizeUncompressed,
2510 (PFNKTEXGETIMAGESIZE)ktxTexture2_GetImageSize,
2511 (PFNKTEXITERATELEVELS)ktxTexture2_IterateLevels,
2512 (PFNKTEXITERATELOADLEVELFACES)ktxTexture2_IterateLoadLevelFaces,
2513 (PFNKTEXNEEDSTRANSCODING)ktxTexture2_NeedsTranscoding,
2514 (PFNKTEXLOADIMAGEDATA)ktxTexture2_LoadImageData,
2515 (PFNKTEXSETIMAGEFROMMEMORY)ktxTexture2_SetImageFromMemory,
2516 (PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture2_SetImageFromStdioStream,
2517 (PFNKTEXWRITETOSTDIOSTREAM)ktxTexture2_WriteToStdioStream,
2518 (PFNKTEXWRITETONAMEDFILE)ktxTexture2_WriteToNamedFile,
2519 (PFNKTEXWRITETOMEMORY)ktxTexture2_WriteToMemory,
2520 (PFNKTEXWRITETOSTREAM)ktxTexture2_WriteToStream,
2521};
2522
2523/** @} */
2524
2525