#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdlib.h>
#include <string.h>
#include <zstd.h>
#include <zstd_errors.h>
#include <KHR/khr_df.h>
#include "dfdutils/dfd.h"
#include "ktx.h"
#include "ktxint.h"
#include "filestream.h"
#include "memstream.h"
#include "texture2.h"
#include "unused.h"
#include "vk_format.h"
#define IS_BIG_ENDIAN 0
struct ktxTexture_vtbl ktxTexture2_vtbl;
struct ktxTexture_vtblInt ktxTexture2_vtblInt;
#if !defined(BITFIELD_ORDER_FROM_MSB)
struct sampleType {
uint32_t bitOffset: 16;
uint32_t bitLength: 8;
uint32_t channelType: 8; uint32_t samplePosition0: 8;
uint32_t samplePosition1: 8;
uint32_t samplePosition2: 8;
uint32_t samplePosition3: 8;
uint32_t lower;
uint32_t upper;
};
struct BDFD {
uint32_t vendorId: 17;
uint32_t descriptorType: 15;
uint32_t versionNumber: 16;
uint32_t descriptorBlockSize: 16;
uint32_t model: 8;
uint32_t primaries: 8;
uint32_t transfer: 8;
uint32_t flags: 8;
uint32_t texelBlockDimension0: 8;
uint32_t texelBlockDimension1: 8;
uint32_t texelBlockDimension2: 8;
uint32_t texelBlockDimension3: 8;
uint32_t bytesPlane0: 8;
uint32_t bytesPlane1: 8;
uint32_t bytesPlane2: 8;
uint32_t bytesPlane3: 8;
uint32_t bytesPlane4: 8;
uint32_t bytesPlane5: 8;
uint32_t bytesPlane6: 8;
uint32_t bytesPlane7: 8;
struct sampleType samples[6];
};
struct BDFD e5b9g9r9_ufloat_comparator = {
.vendorId = 0,
.descriptorType = 0,
.versionNumber = 2,
.descriptorBlockSize = sizeof(struct BDFD),
.model = KHR_DF_MODEL_RGBSDA,
.primaries = KHR_DF_PRIMARIES_BT709,
.transfer = KHR_DF_TRANSFER_LINEAR,
.flags = KHR_DF_FLAG_ALPHA_STRAIGHT,
.texelBlockDimension0 = 0,
.texelBlockDimension1 = 0,
.texelBlockDimension2 = 0,
.texelBlockDimension3 = 0,
.bytesPlane0 = 4,
.bytesPlane1 = 0,
.bytesPlane2 = 0,
.bytesPlane3 = 0,
.bytesPlane4 = 0,
.bytesPlane5 = 0,
.bytesPlane6 = 0,
.bytesPlane7 = 0,
.samples[0] = {
.bitOffset = 0,
.bitLength = 8,
.channelType = KHR_DF_CHANNEL_RGBSDA_RED,
.samplePosition0 = 0,
.samplePosition1 = 0,
.samplePosition2 = 0,
.samplePosition3 = 0,
.lower = 0,
.upper = 8448,
},
.samples[1] = {
.bitOffset = 27,
.bitLength = 4,
.channelType = KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
.samplePosition0 = 0,
.samplePosition1 = 0,
.samplePosition2 = 0,
.samplePosition3 = 0,
.lower = 15,
.upper = 31,
},
.samples[2] = {
.bitOffset = 9,
.bitLength = 8,
.channelType = KHR_DF_CHANNEL_RGBSDA_GREEN,
.samplePosition0 = 0,
.samplePosition1 = 0,
.samplePosition2 = 0,
.samplePosition3 = 0,
.lower = 0,
.upper = 8448,
},
.samples[3] = {
.bitOffset = 27,
.bitLength = 4,
.channelType = KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
.samplePosition0 = 0,
.samplePosition1 = 0,
.samplePosition2 = 0,
.samplePosition3 = 0,
.lower = 15,
.upper = 31,
},
.samples[4] = {
.bitOffset = 18,
.bitLength = 8,
.channelType = KHR_DF_CHANNEL_RGBSDA_BLUE,
.samplePosition0 = 0,
.samplePosition1 = 0,
.samplePosition2 = 0,
.samplePosition3 = 0,
.lower = 0,
.upper = 8448,
},
.samples[5] = {
.bitOffset = 27,
.bitLength = 4,
.channelType = KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
.samplePosition0 = 0,
.samplePosition1 = 0,
.samplePosition2 = 0,
.samplePosition3 = 0,
.lower = 15,
.upper = 31,
}
};
#else
#define shift(x,val) ((val) << KHR_DF_SHIFT_ ## x)
#define sampleshift(x,val) ((val) << KHR_DF_SAMPLESHIFT_ ## x)
#define e5b9g9r9_bdbwordcount KHR_DFDSIZEWORDS(6)
ktx_uint32_t e5b9g9r9_ufloat_comparator[e5b9g9r9_bdbwordcount] = {
0, shift(DESCRIPTORBLOCKSIZE, e5b9g9r9_bdbwordcount * sizeof(ktx_uint32_t)) | shift(VERSIONNUMBER, 2),
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),
0, shift(BYTESPLANE0, 4), 0, sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 0),
0, 0, 8448, sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),
0, 15, 31, sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 9),
0, 0, 8448, sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),
0, 15, 31, sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 18),
0, 0, 8448, sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),
0, 15, 31, };
#endif
bool
ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd)
{
uint32_t* pBdb = pDfd + 1;
if (*pBdb != 0) {
return false;
}
if (KHR_DFDVAL(pBdb, VERSIONNUMBER) != KHR_DF_VERSIONNUMBER_1_3) {
return false;
}
This->blockWidth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION0) + 1;
This->blockHeight = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION1) + 1;
This->blockDepth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION2) + 1;
This->blockSizeInBits = KHR_DFDVAL(pBdb, BYTESPLANE0) * 8;
This->paletteSizeInBits = 0; This->flags = 0;
This->minBlocksX = This->minBlocksY = 1;
if (KHR_DFDVAL(pBdb, MODEL) >= KHR_DF_MODEL_DXT1A) {
This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT;
if (KHR_DFDVAL(pBdb, MODEL) == KHR_DF_MODEL_PVRTC) {
This->minBlocksX = This->minBlocksY = 2;
}
} else {
if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_DEPTH) {
if (KHR_DFDSAMPLECOUNT(pBdb) == 1) {
This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;
} else if (KHR_DFDSAMPLECOUNT(pBdb) == 2) {
This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;
This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;
This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
} else {
return false;
}
} else if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_STENCIL) {
This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;
} else if (KHR_DFDSAMPLECOUNT(pBdb) == 6
#if !defined(BITFIELD_ORDER_FROM_MSB)
&& !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))) {
#else
&& !memcmp(&e5b9g9r9_ufloat_comparator[KHR_DF_WORD_TEXELBLOCKDIMENSION0], &pBdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0], sizeof(e5b9g9r9_ufloat_comparator)-(KHR_DF_WORD_TEXELBLOCKDIMENSION0)*sizeof(uint32_t))) {
#endif
This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
} else {
InterpretedDFDChannel rgba[4];
uint32_t wordBytes;
enum InterpretDFDResult result;
result = interpretDFD(pDfd, &rgba[0], &rgba[1], &rgba[2], &rgba[3],
&wordBytes);
if (result >= i_UNSUPPORTED_ERROR_BIT)
return false;
if (result & i_PACKED_FORMAT_BIT)
This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
}
}
if (This->blockSizeInBits == 0) {
uint32_t blockByteLength;
recreateBytesPlane0FromSampleInfo(pDfd, &blockByteLength);
This->blockSizeInBits = blockByteLength * 8;
}
return true;
}
static KTX_error_code
ktxTexture2_constructCommon(ktxTexture2* This, ktx_uint32_t numLevels)
{
assert(This != NULL);
ktx_size_t privateSize;
This->classId = ktxTexture2_c;
This->vtbl = &ktxTexture2_vtbl;
This->_protected->_vtbl = ktxTexture2_vtblInt;
privateSize = sizeof(ktxTexture2_private)
+ sizeof(ktxLevelIndexEntry) * (numLevels - 1);
This->_private = (ktxTexture2_private*)malloc(privateSize);
if (This->_private == NULL) {
return KTX_OUT_OF_MEMORY;
}
memset(This->_private, 0, privateSize);
return KTX_SUCCESS;
}
static KTX_error_code
ktxTexture2_construct(ktxTexture2* This, ktxTextureCreateInfo* createInfo,
ktxTextureCreateStorageEnum storageAllocation)
{
ktxFormatSize formatSize;
KTX_error_code result;
memset(This, 0, sizeof(*This));
if (createInfo->vkFormat != VK_FORMAT_UNDEFINED) {
This->pDfd = vk2dfd(createInfo->vkFormat);
if (!This->pDfd)
return KTX_INVALID_VALUE; #ifdef _DEBUG
assert(ktxFormatSize_initFromDfd(&formatSize, This->pDfd));
#else
(void)ktxFormatSize_initFromDfd(&formatSize, This->pDfd);
#endif
} else {
This->pDfd = (ktx_uint32_t*)malloc(*createInfo->pDfd);
if (!This->pDfd)
return KTX_OUT_OF_MEMORY;
memcpy(This->pDfd, createInfo->pDfd, *createInfo->pDfd);
if (ktxFormatSize_initFromDfd(&formatSize, This->pDfd)) {
result = KTX_UNSUPPORTED_TEXTURE_TYPE;
goto cleanup;
}
}
result = ktxTexture_construct(ktxTexture(This), createInfo, &formatSize);
if (result != KTX_SUCCESS)
return result;
result = ktxTexture2_constructCommon(This, createInfo->numLevels);
if (result != KTX_SUCCESS)
goto cleanup;;
This->vkFormat = createInfo->vkFormat;
if (This->isCompressed)
This->_protected->_typeSize = 1;
else if (formatSize.flags & KTX_FORMAT_SIZE_PACKED_BIT)
This->_protected->_typeSize = formatSize.blockSizeInBits / 8;
else if (formatSize.flags & (KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT)) {
if (createInfo->vkFormat == VK_FORMAT_D16_UNORM_S8_UINT)
This->_protected->_typeSize = 2;
else
This->_protected->_typeSize = 4;
} else {
uint32_t numComponents;
getDFDComponentInfoUnpacked(This->pDfd, &numComponents,
&This->_protected->_typeSize);
}
This->supercompressionScheme = KTX_SS_NONE;
This->_private->_requiredLevelAlignment
= ktxTexture2_calcRequiredLevelAlignment(This);
ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;
This->_private->_firstLevelFileOffset = 0;
for (ktx_uint32_t level = 0; level < This->numLevels; level++) {
levelIndex[level].uncompressedByteLength =
ktxTexture_calcLevelSize(ktxTexture(This), level,
KTX_FORMAT_VERSION_TWO);
levelIndex[level].byteLength =
levelIndex[level].uncompressedByteLength;
levelIndex[level].byteOffset =
ktxTexture_calcLevelOffset(ktxTexture(This), level);
}
if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) {
This->dataSize
= ktxTexture_calcDataSizeTexture(ktxTexture(This));
This->pData = malloc(This->dataSize);
if (This->pData == NULL) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
}
return result;
cleanup:
ktxTexture2_destruct(This);
return result;
}
static KTX_error_code
ktxTexture2_constructCopy(ktxTexture2* This, ktxTexture2* orig)
{
KTX_error_code result;
memcpy(This, orig, sizeof(ktxTexture2));
This->_protected = NULL;
This->_private = NULL;
This->pDfd = NULL;
This->kvData = NULL;
This->kvDataHead = NULL;
This->pData = NULL;
This->_protected =
(ktxTexture_protected*)malloc(sizeof(ktxTexture_protected));
if (!This->_protected)
return KTX_OUT_OF_MEMORY;
if (!orig->pData && ktxTexture_isActiveStream((ktxTexture*)orig))
ktxTexture2_LoadImageData(orig, NULL, 0);
memcpy(This->_protected, orig->_protected, sizeof(ktxTexture_protected));
ktx_size_t privateSize = sizeof(ktxTexture2_private)
+ sizeof(ktxLevelIndexEntry) * (orig->numLevels - 1);
This->_private = (ktxTexture2_private*)malloc(privateSize);
if (This->_private == NULL) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
memcpy(This->_private, orig->_private, privateSize);
if (orig->_private->_sgdByteLength > 0) {
This->_private->_supercompressionGlobalData
= (ktx_uint8_t*)malloc(orig->_private->_sgdByteLength);
if (!This->_private->_supercompressionGlobalData) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
memcpy(This->_private->_supercompressionGlobalData,
orig->_private->_supercompressionGlobalData,
orig->_private->_sgdByteLength);
}
This->pDfd = (ktx_uint32_t*)malloc(*orig->pDfd);
if (!This->pDfd) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
memcpy(This->pDfd, orig->pDfd, *orig->pDfd);
if (orig->kvDataHead) {
ktxHashList_ConstructCopy(&This->kvDataHead, orig->kvDataHead);
} else if (orig->kvData) {
This->kvData = (ktx_uint8_t*)malloc(orig->kvDataLen);
if (!This->kvData) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
memcpy(This->kvData, orig->kvData, orig->kvDataLen);
}
This->pData = (ktx_uint8_t*)malloc(This->dataSize);
if (This->pData == NULL) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
memcpy(This->pData, orig->pData, orig->dataSize);
return KTX_SUCCESS;
cleanup:
if (This->_protected) free(This->_protected);
if (This->_private) {
if (This->_private->_supercompressionGlobalData)
free(This->_private->_supercompressionGlobalData);
free(This->_private);
}
if (This->pDfd) free (This->pDfd);
if (This->kvDataHead) ktxHashList_Destruct(&This->kvDataHead);
return result;
}
KTX_error_code
ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
KTX_header2* pHeader,
ktxTextureCreateFlags createFlags)
{
ktxTexture2_private* private;
KTX_error_code result;
KTX_supplemental_info suppInfo;
ktxStream* stream;
ktx_size_t levelIndexSize;
assert(pHeader != NULL && pStream != NULL);
memset(This, 0, sizeof(*This));
result = ktxTexture_constructFromStream(ktxTexture(This), pStream,
createFlags);
if (result != KTX_SUCCESS)
return result;
result = ktxCheckHeader2_(pHeader, &suppInfo);
if (result != KTX_SUCCESS)
goto cleanup;
result = ktxTexture2_constructCommon(This, pHeader->levelCount);
if (result != KTX_SUCCESS)
goto cleanup;
private = This->_private;
stream = ktxTexture2_getStream(This);
This->vkFormat = pHeader->vkFormat;
This->supercompressionScheme = pHeader->supercompressionScheme;
This->_protected->_typeSize = pHeader->typeSize;
This->numDimensions = suppInfo.textureDimension;
This->baseWidth = pHeader->pixelWidth;
assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4);
switch (suppInfo.textureDimension) {
case 1:
This->baseHeight = This->baseDepth = 1;
break;
case 2:
This->baseHeight = pHeader->pixelHeight;
This->baseDepth = 1;
break;
case 3:
This->baseHeight = pHeader->pixelHeight;
This->baseDepth = pHeader->pixelDepth;
break;
}
if (pHeader->layerCount > 0) {
This->numLayers = pHeader->layerCount;
This->isArray = KTX_TRUE;
} else {
This->numLayers = 1;
This->isArray = KTX_FALSE;
}
This->numFaces = pHeader->faceCount;
if (pHeader->faceCount == 6)
This->isCubemap = KTX_TRUE;
else
This->isCubemap = KTX_FALSE;
This->numLevels = pHeader->levelCount;
This->generateMipmaps = suppInfo.generateMipmaps;
levelIndexSize = sizeof(ktxLevelIndexEntry) * This->numLevels;
result = stream->read(stream, &private->_levelIndex, levelIndexSize);
if (result != KTX_SUCCESS)
goto cleanup;
private->_firstLevelFileOffset
= private->_levelIndex[This->numLevels-1].byteOffset;
for (ktx_uint32_t level = 0; level < This->numLevels; level++) {
private->_levelIndex[level].byteOffset
-= private->_firstLevelFileOffset;
}
This->pDfd =
(ktx_uint32_t*)malloc(pHeader->dataFormatDescriptor.byteLength);
if (!This->pDfd) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
result = stream->read(stream, This->pDfd,
pHeader->dataFormatDescriptor.byteLength);
if (result != KTX_SUCCESS)
goto cleanup;
if (!ktxFormatSize_initFromDfd(&This->_protected->_formatSize, This->pDfd)) {
result = KTX_UNSUPPORTED_TEXTURE_TYPE;
goto cleanup;
}
This->isCompressed = (This->_protected->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);
This->_private->_requiredLevelAlignment
= ktxTexture2_calcRequiredLevelAlignment(This);
ktxHashList_Construct(&This->kvDataHead);
if (pHeader->keyValueData.byteLength > 0) {
if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) {
ktx_uint32_t kvdLen = pHeader->keyValueData.byteLength;
ktx_uint8_t* pKvd;
pKvd = malloc(kvdLen);
if (pKvd == NULL) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
result = stream->read(stream, pKvd, kvdLen);
if (result != KTX_SUCCESS)
goto cleanup;
if (IS_BIG_ENDIAN) {
ktx_uint8_t* src = pKvd;
ktx_uint8_t* end = pKvd + kvdLen;
while (src < end) {
ktx_uint32_t keyAndValueByteSize = *((ktx_uint32_t*)src);
_ktxSwapEndian32(&keyAndValueByteSize, 1);
src += _KTX_PAD4(keyAndValueByteSize);
}
}
if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) {
char* orientationStr;
ktx_uint32_t orientationLen;
ktx_uint32_t animData[3];
ktx_uint32_t animDataLen;
result = ktxHashList_Deserialize(&This->kvDataHead,
kvdLen, pKvd);
free(pKvd);
if (result != KTX_SUCCESS) {
goto cleanup;
}
result = ktxHashList_FindValue(&This->kvDataHead,
KTX_ORIENTATION_KEY,
&orientationLen,
(void**)&orientationStr);
assert(result != KTX_INVALID_VALUE);
if (result == KTX_SUCCESS) {
if (orientationLen != This->numDimensions + 1) {
result = KTX_FILE_DATA_ERROR;
goto cleanup;
} else {
switch (This->numDimensions) {
case 3:
This->orientation.z = orientationStr[2];
FALLTHROUGH;
case 2:
This->orientation.y = orientationStr[1];
FALLTHROUGH;
case 1:
This->orientation.x = orientationStr[0];
}
}
} else {
result = KTX_SUCCESS; }
result = ktxHashList_FindValue(&This->kvDataHead,
KTX_ANIMDATA_KEY,
&animDataLen,
(void**)animData);
assert(result != KTX_INVALID_VALUE);
if (result == KTX_SUCCESS) {
if (animDataLen != sizeof(animData)) {
result = KTX_FILE_DATA_ERROR;
goto cleanup;
}
if (This->isArray) {
This->isVideo = KTX_TRUE;
This->duration = animData[0];
This->timescale = animData[1];
This->loopcount = animData[2];
} else {
result = KTX_FILE_DATA_ERROR;
goto cleanup;
}
} else {
result = KTX_SUCCESS; }
} else {
This->kvDataLen = kvdLen;
This->kvData = pKvd;
}
} else {
stream->skip(stream, pHeader->keyValueData.byteLength);
}
}
if (pHeader->supercompressionGlobalData.byteLength > 0) {
(void)stream->setpos(stream,
pHeader->supercompressionGlobalData.byteOffset);
private->_supercompressionGlobalData =
(ktx_uint8_t*)malloc(pHeader->supercompressionGlobalData.byteLength);
if (!private->_supercompressionGlobalData) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
private->_sgdByteLength
= pHeader->supercompressionGlobalData.byteLength;
result = stream->read(stream, private->_supercompressionGlobalData,
private->_sgdByteLength);
if (result != KTX_SUCCESS)
goto cleanup;
}
This->dataSize = private->_levelIndex[0].byteOffset
+ private->_levelIndex[0].byteLength;
if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) {
result = ktxTexture2_LoadImageData(This, NULL, 0);
}
if (result != KTX_SUCCESS)
goto cleanup;
return result;
cleanup:
ktxTexture2_destruct(This);
return result;
}
static KTX_error_code
ktxTexture2_constructFromStream(ktxTexture2* This, ktxStream* pStream,
ktxTextureCreateFlags createFlags)
{
KTX_header2 header;
KTX_error_code result;
result = pStream->read(pStream, &header, KTX2_HEADER_SIZE);
if (result != KTX_SUCCESS)
return result;
#if IS_BIG_ENDIAN
#endif
return ktxTexture2_constructFromStreamAndHeader(This, pStream,
&header, createFlags);
}
static KTX_error_code
ktxTexture2_constructFromStdioStream(ktxTexture2* This, FILE* stdioStream,
ktxTextureCreateFlags createFlags)
{
KTX_error_code result;
ktxStream stream;
if (stdioStream == NULL || This == NULL)
return KTX_INVALID_VALUE;
result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);
if (result == KTX_SUCCESS)
result = ktxTexture2_constructFromStream(This, &stream, createFlags);
return result;
}
static KTX_error_code
ktxTexture2_constructFromNamedFile(ktxTexture2* This,
const char* const filename,
ktxTextureCreateFlags createFlags)
{
KTX_error_code result;
ktxStream stream;
FILE* file;
if (This == NULL || filename == NULL)
return KTX_INVALID_VALUE;
file = fopen(filename, "rb");
if (!file)
return KTX_FILE_OPEN_FAILED;
result = ktxFileStream_construct(&stream, file, KTX_TRUE);
if (result == KTX_SUCCESS)
result = ktxTexture2_constructFromStream(This, &stream, createFlags);
return result;
}
static KTX_error_code
ktxTexture2_constructFromMemory(ktxTexture2* This,
const ktx_uint8_t* bytes, ktx_size_t size,
ktxTextureCreateFlags createFlags)
{
KTX_error_code result;
ktxStream stream;
if (bytes == NULL || size == 0)
return KTX_INVALID_VALUE;
result = ktxMemStream_construct_ro(&stream, bytes, size);
if (result == KTX_SUCCESS)
result = ktxTexture2_constructFromStream(This, &stream, createFlags);
return result;
}
void
ktxTexture2_destruct(ktxTexture2* This)
{
if (This->pDfd) free(This->pDfd);
if (This->_private) {
ktx_uint8_t* sgd = This->_private->_supercompressionGlobalData;
if (sgd) free(sgd);
free(This->_private);
}
ktxTexture_destruct(ktxTexture(This));
}
KTX_error_code
ktxTexture2_Create(ktxTextureCreateInfo* createInfo,
ktxTextureCreateStorageEnum storageAllocation,
ktxTexture2** newTex)
{
KTX_error_code result;
if (newTex == NULL)
return KTX_INVALID_VALUE;
ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
if (tex == NULL)
return KTX_OUT_OF_MEMORY;
result = ktxTexture2_construct(tex, createInfo, storageAllocation);
if (result != KTX_SUCCESS) {
free(tex);
} else {
*newTex = tex;
}
return result;
}
KTX_error_code
ktxTexture2_CreateCopy(ktxTexture2* orig, ktxTexture2** newTex)
{
KTX_error_code result;
if (newTex == NULL)
return KTX_INVALID_VALUE;
ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
if (tex == NULL)
return KTX_OUT_OF_MEMORY;
result = ktxTexture2_constructCopy(tex, orig);
if (result != KTX_SUCCESS) {
free(tex);
} else {
*newTex = tex;
}
return result;
}
KTX_error_code
ktxTexture2_CreateFromStdioStream(FILE* stdioStream,
ktxTextureCreateFlags createFlags,
ktxTexture2** newTex)
{
KTX_error_code result;
if (newTex == NULL)
return KTX_INVALID_VALUE;
ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
if (tex == NULL)
return KTX_OUT_OF_MEMORY;
result = ktxTexture2_constructFromStdioStream(tex, stdioStream,
createFlags);
if (result == KTX_SUCCESS)
*newTex = (ktxTexture2*)tex;
else {
free(tex);
*newTex = NULL;
}
return result;
}
KTX_error_code
ktxTexture2_CreateFromNamedFile(const char* const filename,
ktxTextureCreateFlags createFlags,
ktxTexture2** newTex)
{
KTX_error_code result;
if (newTex == NULL)
return KTX_INVALID_VALUE;
ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
if (tex == NULL)
return KTX_OUT_OF_MEMORY;
result = ktxTexture2_constructFromNamedFile(tex, filename, createFlags);
if (result == KTX_SUCCESS)
*newTex = (ktxTexture2*)tex;
else {
free(tex);
*newTex = NULL;
}
return result;
}
KTX_error_code
ktxTexture2_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
ktxTextureCreateFlags createFlags,
ktxTexture2** newTex)
{
KTX_error_code result;
if (newTex == NULL)
return KTX_INVALID_VALUE;
ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
if (tex == NULL)
return KTX_OUT_OF_MEMORY;
result = ktxTexture2_constructFromMemory(tex, bytes, size,
createFlags);
if (result == KTX_SUCCESS)
*newTex = (ktxTexture2*)tex;
else {
free(tex);
*newTex = NULL;
}
return result;
}
KTX_error_code
ktxTexture2_CreateFromStream(ktxStream* stream,
ktxTextureCreateFlags createFlags,
ktxTexture2** newTex)
{
KTX_error_code result;
if (newTex == NULL)
return KTX_INVALID_VALUE;
ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
if (tex == NULL)
return KTX_OUT_OF_MEMORY;
result = ktxTexture2_constructFromStream(tex, stream, createFlags);
if (result == KTX_SUCCESS)
*newTex = (ktxTexture2*)tex;
else {
free(tex);
*newTex = NULL;
}
return result;
}
void
ktxTexture2_Destroy(ktxTexture2* This)
{
ktxTexture2_destruct(This);
free(This);
}
ktx_size_t
ktxTexture2_calcDataSizeLevels(ktxTexture2* This, ktx_uint32_t levels)
{
ktx_size_t dataSize = 0;
assert(This != NULL);
assert(This->supercompressionScheme == KTX_SS_NONE);
assert(levels <= This->numLevels);
for (ktx_uint32_t i = levels - 1; i > 0; i--) {
ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,
KTX_FORMAT_VERSION_TWO);
dataSize += _KTX_PADN(This->_private->_requiredLevelAlignment,
levelSize);
}
dataSize += ktxTexture_calcLevelSize(ktxTexture(This), 0,
KTX_FORMAT_VERSION_TWO);
return dataSize;
}
ktx_size_t
ktxTexture2_calcFaceLodSize(ktxTexture2* This, ktx_uint32_t level)
{
assert(This != NULL);
assert(This->supercompressionScheme == KTX_SS_NONE);
if (This->isCubemap && !This->isArray)
return ktxTexture_calcImageSize(ktxTexture(This), level,
KTX_FORMAT_VERSION_TWO);
else
return This->_private->_levelIndex[level].uncompressedByteLength;
}
ktx_size_t
ktxTexture2_calcLevelOffset(ktxTexture2* This, ktx_uint32_t level)
{
assert (This != NULL);
assert(This->supercompressionScheme == KTX_SS_NONE);
assert (level < This->numLevels);
ktx_size_t levelOffset = 0;
for (ktx_uint32_t i = This->numLevels - 1; i > level; i--) {
ktx_size_t levelSize;
levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,
KTX_FORMAT_VERSION_TWO);
levelOffset += _KTX_PADN(This->_private->_requiredLevelAlignment,
levelSize);
}
return levelOffset;
}
ktx_uint64_t ktxTexture2_levelFileOffset(ktxTexture2* This, ktx_uint32_t level)
{
assert(This->_private->_firstLevelFileOffset != 0);
return This->_private->_levelIndex[level].byteOffset
+ This->_private->_firstLevelFileOffset;
}
static uint32_t
gcd(uint32_t a, uint32_t b) {
if (a == 0)
return b;
return gcd(b % a, a);
}
uint32_t
lcm4(uint32_t a)
{
if (!(a & 0x03))
return a; return (a*4) / gcd(a, 4);
}
ktx_uint32_t
ktxTexture2_calcRequiredLevelAlignment(ktxTexture2* This)
{
ktx_uint32_t alignment;
if (This->supercompressionScheme != KTX_SS_NONE)
alignment = 1;
else if (This->vkFormat != VK_FORMAT_UNDEFINED)
alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);
else
alignment = 16;
return alignment;
}
ktx_uint32_t
ktxTexture2_calcPostInflationLevelAlignment(ktxTexture2* This)
{
ktx_uint32_t alignment;
assert(This->supercompressionScheme >= KTX_SS_ZSTD);
if (This->vkFormat != VK_FORMAT_UNDEFINED)
alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);
else
alignment = 16;
return alignment;
}
void
ktxTexture2_GetComponentInfo(ktxTexture2* This, uint32_t* pNumComponents,
uint32_t* pComponentByteLength)
{
getDFDComponentInfoUnpacked(This->pDfd, pNumComponents,
pComponentByteLength);
}
ktx_uint32_t
ktxTexture2_GetNumComponents(ktxTexture2* This)
{
uint32_t* pBdb = This->pDfd + 1;
uint32_t dfdNumComponents = getDFDNumComponents(This->pDfd);
uint32_t colorModel = KHR_DFDVAL(pBdb, MODEL);
if (colorModel < KHR_DF_MODEL_DXT1A) {
return dfdNumComponents;
} else {
switch (colorModel) {
case KHR_DF_MODEL_ETC1S:
{
uint32_t channel0Id = KHR_DFDSVAL(pBdb, 0, CHANNELID);
if (dfdNumComponents == 1) {
if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB)
return 3;
else
return 1;
} else {
uint32_t channel1Id = KHR_DFDSVAL(pBdb, 1, CHANNELID);
if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB
&& channel1Id == KHR_DF_CHANNEL_ETC1S_AAA)
return 4;
else {
assert(channel0Id == KHR_DF_CHANNEL_ETC1S_RRR
&& channel1Id == KHR_DF_CHANNEL_ETC1S_GGG);
return 2;
}
}
break;
}
case KHR_DF_MODEL_UASTC:
switch (KHR_DFDSVAL(pBdb, 0, CHANNELID)) {
case KHR_DF_CHANNEL_UASTC_RRR:
return 1;
case KHR_DF_CHANNEL_UASTC_RRRG:
return 2;
case KHR_DF_CHANNEL_UASTC_RGB:
return 3;
case KHR_DF_CHANNEL_UASTC_RGBA:
return 4;
default:
assert(false);
return 1;
}
break;
default:
return dfdNumComponents;
}
}
}
KTX_error_code
ktxTexture2_GetImageOffset(ktxTexture2* This, ktx_uint32_t level,
ktx_uint32_t layer, ktx_uint32_t faceSlice,
ktx_size_t* pOffset)
{
if (This == NULL)
return KTX_INVALID_VALUE;
if (level >= This->numLevels || layer >= This->numLayers)
return KTX_INVALID_OPERATION;
if (This->supercompressionScheme != KTX_SS_NONE)
return KTX_INVALID_OPERATION;
if (This->isCubemap) {
if (faceSlice >= This->numFaces)
return KTX_INVALID_OPERATION;
} else {
ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level);
if (faceSlice >= maxSlice)
return KTX_INVALID_OPERATION;
}
*pOffset = ktxTexture2_levelDataOffset(This, level);
if (layer != 0) {
ktx_size_t layerSize;
layerSize = ktxTexture_layerSize(ktxTexture(This), level,
KTX_FORMAT_VERSION_TWO);
*pOffset += layer * layerSize;
}
if (faceSlice != 0) {
ktx_size_t imageSize;
imageSize = ktxTexture2_GetImageSize(This, level);
*pOffset += faceSlice * imageSize;
}
return KTX_SUCCESS;
}
ktx_uint32_t
ktxTexture2_GetOETF(ktxTexture2* This)
{
return KHR_DFDVAL(This->pDfd+1, TRANSFER);
}
ktx_bool_t
ktxTexture2_GetPremultipliedAlpha(ktxTexture2* This)
{
return KHR_DFDVAL(This->pDfd+1, FLAGS) & KHR_DF_FLAG_ALPHA_PREMULTIPLIED;
}
ktx_bool_t
ktxTexture2_NeedsTranscoding(ktxTexture2* This)
{
if (This->supercompressionScheme == KTX_SS_BASIS_LZ)
return true;
else if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_UASTC)
return true;
else
return false;
}
ktx_size_t
ktxTexture2_GetDataSizeUncompressed(ktxTexture2* This)
{
switch (This->supercompressionScheme) {
case KTX_SS_BASIS_LZ:
case KTX_SS_NONE:
return This->dataSize;
case KTX_SS_ZSTD:
{
ktx_size_t uncompressedSize = 0;
ktx_uint32_t uncompressedLevelAlignment;
ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;
uncompressedLevelAlignment =
ktxTexture2_calcPostInflationLevelAlignment(This);
for (ktx_int32_t level = This->numLevels - 1; level >= 1; level--) {
ktx_size_t uncompressedLevelSize;
uncompressedLevelSize = levelIndex[level].uncompressedByteLength;
uncompressedLevelSize = _KTX_PADN(uncompressedLevelAlignment,
uncompressedLevelSize);
uncompressedSize += uncompressedLevelSize;
}
uncompressedSize += levelIndex[0].uncompressedByteLength;
return uncompressedSize;
}
case KTX_SS_BEGIN_VENDOR_RANGE:
case KTX_SS_END_VENDOR_RANGE:
case KTX_SS_BEGIN_RESERVED:
default:
return 0;
}
}
ktx_size_t
ktxTexture2_GetImageSize(ktxTexture2* This, ktx_uint32_t level)
{
return ktxTexture_calcImageSize(ktxTexture(This), level,
KTX_FORMAT_VERSION_TWO);
}
KTX_error_code
ktxTexture2_IterateLevels(ktxTexture2* This, PFNKTXITERCB iterCb, void* userdata)
{
KTX_error_code result = KTX_SUCCESS;
ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;
if (This == NULL)
return KTX_INVALID_VALUE;
if (iterCb == NULL)
return KTX_INVALID_VALUE;
if (This->supercompressionScheme != KTX_SS_NONE)
return KTX_INVALID_OPERATION;
for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)
{
ktx_uint32_t width, height, depth;
ktx_uint64_t levelSize;
ktx_uint64_t offset;
width = MAX(1, This->baseWidth >> level);
height = MAX(1, This->baseHeight >> level);
depth = MAX(1, This->baseDepth >> level);
levelSize = levelIndex[level].uncompressedByteLength;
offset = ktxTexture2_levelDataOffset(This, level);
result = iterCb(level, 0, width, height, depth,
levelSize, This->pData + offset, userdata);
if (result != KTX_SUCCESS)
break;
}
return result;
}
KTX_error_code
ktxTexture2_IterateLoadLevelFaces(ktxTexture2* This, PFNKTXITERCB iterCb,
void* userdata)
{
DECLARE_PROTECTED(ktxTexture);
ktxStream* stream = (ktxStream *)&prtctd->_stream;
ktxLevelIndexEntry* levelIndex;
ktx_size_t dataSize = 0, uncompressedDataSize = 0;
KTX_error_code result = KTX_SUCCESS;
ktx_uint8_t* dataBuf = NULL;
ktx_uint8_t* uncompressedDataBuf = NULL;
ktx_uint8_t* pData;
ZSTD_DCtx* dctx = NULL;
if (This == NULL)
return KTX_INVALID_VALUE;
if (This->classId != ktxTexture2_c)
return KTX_INVALID_OPERATION;
if (This->supercompressionScheme != KTX_SS_NONE &&
This->supercompressionScheme != KTX_SS_ZSTD)
return KTX_INVALID_OPERATION;
if (iterCb == NULL)
return KTX_INVALID_VALUE;
if (prtctd->_stream.data.file == NULL)
return KTX_INVALID_OPERATION;
levelIndex = This->_private->_levelIndex;
dataSize = levelIndex[0].byteLength;
dataBuf = malloc(dataSize);
if (!dataBuf)
return KTX_OUT_OF_MEMORY;
if (This->supercompressionScheme == KTX_SS_ZSTD) {
uncompressedDataSize = levelIndex[0].uncompressedByteLength;
uncompressedDataBuf = malloc(uncompressedDataSize);
if (!uncompressedDataBuf) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
dctx = ZSTD_createDCtx();
pData = uncompressedDataBuf;
} else {
pData = dataBuf;
}
for (ktx_int32_t level = This->numLevels - 1; level >= 0; --level)
{
ktx_size_t levelSize;
GLsizei width, height, depth;
width = MAX(1, This->baseWidth >> level);
height = MAX(1, This->baseHeight >> level);
depth = MAX(1, This->baseDepth >> level);
levelSize = levelIndex[level].byteLength;
if (dataSize < levelSize) {
result = KTX_FILE_DATA_ERROR;
goto cleanup;
}
result = stream->setpos(stream,
ktxTexture2_levelFileOffset(This, level));
if (result != KTX_SUCCESS)
goto cleanup;
result = stream->read(stream, dataBuf, levelSize);
if (result != KTX_SUCCESS)
goto cleanup;
if (This->supercompressionScheme == KTX_SS_ZSTD) {
levelSize =
ZSTD_decompressDCtx(dctx, uncompressedDataBuf,
uncompressedDataSize,
dataBuf,
levelSize);
if (ZSTD_isError(levelSize)) {
ZSTD_ErrorCode error = ZSTD_getErrorCode(levelSize);
switch(error) {
case ZSTD_error_dstSize_tooSmall:
return KTX_INVALID_VALUE; case ZSTD_error_memory_allocation:
return KTX_OUT_OF_MEMORY;
default:
return KTX_FILE_DATA_ERROR;
}
}
}
#if IS_BIG_ENDIAN
switch (prtctd->_typeSize) {
case 2:
_ktxSwapEndian16((ktx_uint16_t*)pData, levelSize / 2);
break;
case 4:
_ktxSwapEndian32((ktx_uint32_t*)pDest, levelSize / 4);
break;
case 8:
_ktxSwapEndian64((ktx_uint64_t*)pDest, levelSize / 8);
break;
}
#endif
if (This->isCubemap && !This->isArray) {
ktx_uint8_t* pFace = pData;
struct blockCount {
ktx_uint32_t x, y;
} blockCount;
ktx_size_t faceSize;
blockCount.x
= (uint32_t)ceilf((float)width / prtctd->_formatSize.blockWidth);
blockCount.y
= (uint32_t)ceilf((float)height / prtctd->_formatSize.blockHeight);
blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x);
blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y);
faceSize = blockCount.x * blockCount.y
* prtctd->_formatSize.blockSizeInBits / 8;
for (ktx_uint32_t face = 0; face < This->numFaces; ++face) {
result = iterCb(level, face,
width, height, depth,
(ktx_uint32_t)faceSize, pFace, userdata);
pFace += faceSize;
if (result != KTX_SUCCESS)
goto cleanup;
}
} else {
result = iterCb(level, 0,
width, height, depth,
(ktx_uint32_t)levelSize, pData, userdata);
if (result != KTX_SUCCESS)
goto cleanup;
}
}
stream->destruct(stream);
This->_private->_firstLevelFileOffset = 0;
cleanup:
free(dataBuf);
if (uncompressedDataBuf) free(uncompressedDataBuf);
if (dctx) ZSTD_freeDCtx(dctx);
return result;
}
KTX_error_code
ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
ktx_uint8_t* pInflatedData,
ktx_size_t inflatedDataCapacity);
KTX_error_code
ktxTexture2_LoadImageData(ktxTexture2* This,
ktx_uint8_t* pBuffer, ktx_size_t bufSize)
{
DECLARE_PROTECTED(ktxTexture);
DECLARE_PRIVATE(ktxTexture2);
ktx_uint8_t* pDest;
ktx_uint8_t* pDeflatedData = 0;
ktx_uint8_t* pReadBuf;
KTX_error_code result = KTX_SUCCESS;
ktx_size_t inflatedDataCapacity = ktxTexture2_GetDataSizeUncompressed(This);
if (This == NULL)
return KTX_INVALID_VALUE;
if (This->pData != NULL)
return KTX_INVALID_OPERATION;
if (prtctd->_stream.data.file == NULL)
return KTX_INVALID_OPERATION;
if (pBuffer == NULL) {
This->pData = malloc(inflatedDataCapacity);
if (This->pData == NULL)
return KTX_OUT_OF_MEMORY;
pDest = This->pData;
} else if (bufSize < inflatedDataCapacity) {
return KTX_INVALID_VALUE;
} else {
pDest = pBuffer;
}
if (This->supercompressionScheme == KTX_SS_ZSTD) {
pDeflatedData = malloc(This->dataSize);
if (pDeflatedData == NULL)
return KTX_OUT_OF_MEMORY;
pReadBuf = pDeflatedData;
} else {
pReadBuf = pDest;
}
result = prtctd->_stream.setpos(&prtctd->_stream,
private->_firstLevelFileOffset);
if (result != KTX_SUCCESS)
return result;
result = prtctd->_stream.read(&prtctd->_stream, pReadBuf,
This->dataSize);
if (result != KTX_SUCCESS)
return result;
if (This->supercompressionScheme == KTX_SS_ZSTD) {
assert(pDeflatedData != NULL);
result = ktxTexture2_inflateZstdInt(This, pDeflatedData, pDest,
inflatedDataCapacity);
free(pDeflatedData);
if (result != KTX_SUCCESS) {
if (pBuffer == NULL) {
free(This->pData);
This->pData = 0;
}
return result;
}
}
if (IS_BIG_ENDIAN) {
for (ktx_uint32_t level = 0; level < This->numLevels; ++level)
{
ktx_size_t levelOffset;
ktx_size_t levelByteLength;
levelByteLength = private->_levelIndex[level].byteLength;
levelOffset = ktxTexture2_levelDataOffset(This, level);
pDest = This->pData + levelOffset;
switch (prtctd->_typeSize) {
case 2:
_ktxSwapEndian16((ktx_uint16_t*)pDest, levelByteLength / 2);
break;
case 4:
_ktxSwapEndian32((ktx_uint32_t*)pDest, levelByteLength / 4);
break;
case 8:
_ktxSwapEndian64((ktx_uint64_t*)pDest, levelByteLength / 8);
break;
}
}
}
prtctd->_stream.destruct(&prtctd->_stream);
private->_firstLevelFileOffset = 0;
return result;
}
ktx_uint64_t ktxTexture2_levelDataOffset(ktxTexture2* This, ktx_uint32_t level)
{
return This->_private->_levelIndex[level].byteOffset;
}
KTX_error_code
ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
ktx_uint8_t* pInflatedData,
ktx_size_t inflatedDataCapacity)
{
DECLARE_PROTECTED(ktxTexture);
ktx_uint32_t levelIndexByteLength =
This->numLevels * sizeof(ktxLevelIndexEntry);
uint32_t levelOffset = 0;
ktxLevelIndexEntry* cindex = This->_private->_levelIndex;
ktxLevelIndexEntry* nindex;
ktx_uint32_t uncompressedLevelAlignment;
ZSTD_DCtx* dctx;
if (pDeflatedData == NULL)
return KTX_INVALID_VALUE;
if (pInflatedData == NULL)
return KTX_INVALID_VALUE;
if (This->supercompressionScheme != KTX_SS_ZSTD)
return KTX_INVALID_OPERATION;
nindex = malloc(levelIndexByteLength);
if (nindex == NULL)
return KTX_OUT_OF_MEMORY;
uncompressedLevelAlignment =
ktxTexture2_calcPostInflationLevelAlignment(This);
ktx_size_t inflatedByteLength = 0;
dctx = ZSTD_createDCtx();
for (int32_t level = This->numLevels - 1; level >= 0; level--) {
size_t levelByteLength =
ZSTD_decompressDCtx(dctx, pInflatedData + levelOffset,
inflatedDataCapacity,
&pDeflatedData[cindex[level].byteOffset],
cindex[level].byteLength);
if (ZSTD_isError(levelByteLength)) {
ZSTD_ErrorCode error = ZSTD_getErrorCode(levelByteLength);
switch(error) {
case ZSTD_error_dstSize_tooSmall:
return KTX_INVALID_VALUE; case ZSTD_error_memory_allocation:
return KTX_OUT_OF_MEMORY;
default:
return KTX_FILE_DATA_ERROR;
}
}
nindex[level].byteOffset = levelOffset;
nindex[level].uncompressedByteLength = nindex[level].byteLength =
levelByteLength;
inflatedByteLength += levelByteLength;
levelOffset += _KTX_PADN(uncompressedLevelAlignment, levelByteLength);
inflatedDataCapacity -= levelByteLength;
}
ZSTD_freeDCtx(dctx);
This->dataSize = inflatedByteLength;
This->supercompressionScheme = KTX_SS_NONE;
memcpy(cindex, nindex, levelIndexByteLength); free(nindex);
This->_private->_requiredLevelAlignment = uncompressedLevelAlignment;
uint32_t* bdb = This->pDfd + 1;
bdb[KHR_DF_WORD_BYTESPLANE0] = prtctd->_formatSize.blockSizeInBits / 8;
return KTX_SUCCESS;
}
#if !KTX_FEATURE_WRITE
KTX_error_code
ktxTexture2_SetImageFromMemory(ktxTexture2* This, ktx_uint32_t level,
ktx_uint32_t layer, ktx_uint32_t faceSlice,
const ktx_uint8_t* src, ktx_size_t srcSize)
{
UNUSED(This);
UNUSED(level);
UNUSED(layer);
UNUSED(faceSlice);
UNUSED(src);
UNUSED(srcSize);
return KTX_INVALID_OPERATION;
}
KTX_error_code
ktxTexture2_SetImageFromStdioStream(ktxTexture2* This, ktx_uint32_t level,
ktx_uint32_t layer, ktx_uint32_t faceSlice,
FILE* src, ktx_size_t srcSize)
{
UNUSED(This);
UNUSED(level);
UNUSED(layer);
UNUSED(faceSlice);
UNUSED(src);
UNUSED(srcSize);
return KTX_INVALID_OPERATION;
}
KTX_error_code
ktxTexture2_WriteToStdioStream(ktxTexture2* This, FILE* dstsstr)
{
UNUSED(This);
UNUSED(dstsstr);
return KTX_INVALID_OPERATION;
}
KTX_error_code
ktxTexture2_WriteToNamedFile(ktxTexture2* This, const char* const dstname)
{
UNUSED(This);
UNUSED(dstname);
return KTX_INVALID_OPERATION;
}
KTX_error_code
ktxTexture2_WriteToMemory(ktxTexture2* This,
ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)
{
UNUSED(This);
UNUSED(ppDstBytes);
UNUSED(pSize);
return KTX_INVALID_OPERATION;
}
KTX_error_code
ktxTexture2_WriteToStream(ktxTexture2* This,
ktxStream* dststr)
{
UNUSED(This);
UNUSED(dststr);
return KTX_INVALID_OPERATION;
}
#endif
struct ktxTexture_vtblInt ktxTexture2_vtblInt = {
(PFNCALCDATASIZELEVELS)ktxTexture2_calcDataSizeLevels,
(PFNCALCFACELODSIZE)ktxTexture2_calcFaceLodSize,
(PFNCALCLEVELOFFSET)ktxTexture2_calcLevelOffset
};
struct ktxTexture_vtbl ktxTexture2_vtbl = {
(PFNKTEXDESTROY)ktxTexture2_Destroy,
(PFNKTEXGETIMAGEOFFSET)ktxTexture2_GetImageOffset,
(PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture2_GetDataSizeUncompressed,
(PFNKTEXGETIMAGESIZE)ktxTexture2_GetImageSize,
(PFNKTEXITERATELEVELS)ktxTexture2_IterateLevels,
(PFNKTEXITERATELOADLEVELFACES)ktxTexture2_IterateLoadLevelFaces,
(PFNKTEXNEEDSTRANSCODING)ktxTexture2_NeedsTranscoding,
(PFNKTEXLOADIMAGEDATA)ktxTexture2_LoadImageData,
(PFNKTEXSETIMAGEFROMMEMORY)ktxTexture2_SetImageFromMemory,
(PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture2_SetImageFromStdioStream,
(PFNKTEXWRITETOSTDIOSTREAM)ktxTexture2_WriteToStdioStream,
(PFNKTEXWRITETONAMEDFILE)ktxTexture2_WriteToNamedFile,
(PFNKTEXWRITETOMEMORY)ktxTexture2_WriteToMemory,
(PFNKTEXWRITETOSTREAM)ktxTexture2_WriteToStream,
};