summaryrefslogtreecommitdiffstats
path: root/thirdparty/libktx/lib/texture2.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/libktx/lib/texture2.c')
-rw-r--r--thirdparty/libktx/lib/texture2.c350
1 files changed, 298 insertions, 52 deletions
diff --git a/thirdparty/libktx/lib/texture2.c b/thirdparty/libktx/lib/texture2.c
index afbe7dcbfe..173e5026d9 100644
--- a/thirdparty/libktx/lib/texture2.c
+++ b/thirdparty/libktx/lib/texture2.c
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include <zstd.h>
#include <zstd_errors.h>
#include <KHR/khr_df.h>
@@ -293,6 +294,10 @@ ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd)
return false;
if (result & i_PACKED_FORMAT_BIT)
This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
+ if (result & i_COMPRESSED_FORMAT_BIT)
+ This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT;
+ if (result & i_YUVSDA_FORMAT_BIT)
+ This->flags |= KTX_FORMAT_SIZE_YUVSDA_BIT;
}
}
if (This->blockSizeInBits == 0) {
@@ -325,20 +330,7 @@ ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd)
static uint32_t*
ktxVk2dfd(ktx_uint32_t vkFormat)
{
- switch(vkFormat) {
- case VK_FORMAT_D16_UNORM_S8_UINT:
- // 2 16-bit words. D16 in the first. S8 in the 8 LSBs of the second.
- return createDFDDepthStencil(16, 8, 4);
- case VK_FORMAT_D24_UNORM_S8_UINT:
- // 1 32-bit word. D24 in the MSBs. S8 in the LSBs.
- return createDFDDepthStencil(24, 8, 4);
- case VK_FORMAT_D32_SFLOAT_S8_UINT:
- // 2 32-bit words. D32 float in the first word. S8 in LSBs of the
- // second.
- return createDFDDepthStencil(32, 8, 8);
- default:
- return vk2dfd(vkFormat);
- }
+ return vk2dfd(vkFormat);
}
/**
@@ -419,7 +411,7 @@ ktxTexture2_construct(ktxTexture2* This, ktxTextureCreateInfo* createInfo,
if (!This->pDfd)
return KTX_OUT_OF_MEMORY;
memcpy(This->pDfd, createInfo->pDfd, *createInfo->pDfd);
- if (ktxFormatSize_initFromDfd(&formatSize, This->pDfd)) {
+ if (!ktxFormatSize_initFromDfd(&formatSize, This->pDfd)) {
result = KTX_UNSUPPORTED_TEXTURE_TYPE;
goto cleanup;
}
@@ -437,15 +429,26 @@ ktxTexture2_construct(ktxTexture2* This, ktxTextureCreateInfo* createInfo,
// Ideally we'd set all these things in ktxFormatSize_initFromDfd
// but This->_protected is not allocated until ktxTexture_construct;
- if (This->isCompressed)
+ if (This->isCompressed && (formatSize.flags & KTX_FORMAT_SIZE_YUVSDA_BIT) == 0) {
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)
+ } else if (formatSize.flags & (KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT)) {
+ switch (createInfo->vkFormat) {
+ case VK_FORMAT_S8_UINT:
+ This->_protected->_typeSize = 1;
+ break;
+ case VK_FORMAT_D16_UNORM: // [[fallthrough]];
+ case VK_FORMAT_D16_UNORM_S8_UINT:
This->_protected->_typeSize = 2;
- else
+ break;
+ case VK_FORMAT_X8_D24_UNORM_PACK32: // [[fallthrough]];
+ case VK_FORMAT_D24_UNORM_S8_UINT: // [[fallthrough]];
+ case VK_FORMAT_D32_SFLOAT: // [[fallthrough]];
+ case VK_FORMAT_D32_SFLOAT_S8_UINT:
This->_protected->_typeSize = 4;
+ break;
+ }
+ } else if (formatSize.flags & KTX_FORMAT_SIZE_PACKED_BIT) {
+ This->_protected->_typeSize = formatSize.blockSizeInBits / 8;
} else {
// Unpacked and uncompressed
uint32_t numComponents;
@@ -503,7 +506,7 @@ cleanup:
*
* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.
*/
-static KTX_error_code
+KTX_error_code
ktxTexture2_constructCopy(ktxTexture2* This, ktxTexture2* orig)
{
KTX_error_code result;
@@ -648,6 +651,7 @@ ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
KTX_error_code result;
KTX_supplemental_info suppInfo;
ktxStream* stream;
+ struct BDFD* pBDFD;
ktx_size_t levelIndexSize;
assert(pHeader != NULL && pStream != NULL);
@@ -721,9 +725,21 @@ ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
for (ktx_uint32_t level = 0; level < This->numLevels; level++) {
private->_levelIndex[level].byteOffset
-= private->_firstLevelFileOffset;
+ if (This->supercompressionScheme == KTX_SS_NONE &&
+ private->_levelIndex[level].byteLength != private->_levelIndex[level].uncompressedByteLength) {
+ // For non-supercompressed files the levels must have matching byte lengths
+ result = KTX_FILE_DATA_ERROR;
+ }
}
+ if (result != KTX_SUCCESS)
+ goto cleanup;
// Read DFD
+ if (pHeader->dataFormatDescriptor.byteOffset == 0 || pHeader->dataFormatDescriptor.byteLength < 16) {
+ // Missing or too small DFD
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
This->pDfd =
(ktx_uint32_t*)malloc(pHeader->dataFormatDescriptor.byteLength);
if (!This->pDfd) {
@@ -735,19 +751,87 @@ ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
if (result != KTX_SUCCESS)
goto cleanup;
+ if (pHeader->dataFormatDescriptor.byteLength != This->pDfd[0]) {
+ // DFD byteLength does not match dfdTotalSize
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ pBDFD = (struct BDFD*)(This->pDfd + 1);
+ if (pBDFD->descriptorBlockSize < 24 || (pBDFD->descriptorBlockSize - 24) % 16 != 0) {
+ // BDFD has invalid size
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ if (pBDFD->transfer != KHR_DF_TRANSFER_LINEAR && pBDFD->transfer != KHR_DF_TRANSFER_SRGB) {
+ // Unsupported transfer function
+ result = KTX_FILE_DATA_ERROR;
+ 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);
- if (This->supercompressionScheme == KTX_SS_BASIS_LZ
- && KHR_DFDVAL(This->pDfd + 1, MODEL) != KHR_DF_MODEL_ETC1S)
- {
+ if (This->supercompressionScheme == KTX_SS_BASIS_LZ && pBDFD->model != KHR_DF_MODEL_ETC1S) {
result = KTX_FILE_DATA_ERROR;
goto cleanup;
}
+ // Check compatibility with the KHR_texture_basisu glTF extension, if needed.
+ if (createFlags & KTX_TEXTURE_CREATE_CHECK_GLTF_BASISU_BIT) {
+ uint32_t max_dim = MAX(MAX(pHeader->pixelWidth, pHeader->pixelHeight), pHeader->pixelDepth);
+ uint32_t full_mip_pyramid_level_count = 1 + (uint32_t)log2(max_dim);
+ if (pHeader->levelCount != 1 && pHeader->levelCount != full_mip_pyramid_level_count) {
+ // KHR_texture_basisu requires full mip pyramid or single mip level
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ if (This->numDimensions != 2 || This->isArray || This->isCubemap) {
+ // KHR_texture_basisu requires 2D textures.
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ if ((This->baseWidth % 4) != 0 || (This->baseHeight % 4) != 0) {
+ // KHR_texture_basisu requires width and height to be a multiple of 4.
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ if (pBDFD->model != KHR_DF_MODEL_ETC1S && pBDFD->model != KHR_DF_MODEL_UASTC) {
+ // KHR_texture_basisu requires BasisLZ or UASTC
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ if (pBDFD->model == KHR_DF_MODEL_UASTC &&
+ This->supercompressionScheme != KTX_SS_NONE &&
+ This->supercompressionScheme != KTX_SS_ZSTD) {
+ // KHR_texture_basisu only allows NONE and ZSTD supercompression for UASTC
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ }
+
+ uint32_t sampleCount = KHR_DFDSAMPLECOUNT(This->pDfd + 1);
+ if (sampleCount == 0) {
+ // Invalid sample count
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ if (pBDFD->model == KHR_DF_MODEL_ETC1S || pBDFD->model == KHR_DF_MODEL_UASTC) {
+ if (sampleCount < 1 || sampleCount > 2 || (sampleCount == 2 && pBDFD->model == KHR_DF_MODEL_UASTC)) {
+ // Invalid sample count
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ if (pBDFD->texelBlockDimension0 != 3 || pBDFD->texelBlockDimension1 != 3 ||
+ pBDFD->texelBlockDimension2 != 0 || pBDFD->texelBlockDimension3 != 0) {
+ // Texel block dimension must be 4x4x1x1 (offset by one)
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
+ }
+
This->_private->_requiredLevelAlignment
= ktxTexture2_calcRequiredLevelAlignment(This);
@@ -755,6 +839,12 @@ ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
ktxHashList_Construct(&This->kvDataHead);
// Load KVData.
if (pHeader->keyValueData.byteLength > 0) {
+ uint32_t expectedOffset = pHeader->dataFormatDescriptor.byteOffset + pHeader->dataFormatDescriptor.byteLength;
+ expectedOffset = (expectedOffset + 3) & ~0x3; // 4 byte aligned
+ if (pHeader->keyValueData.byteOffset != expectedOffset) {
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ }
if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) {
ktx_uint32_t kvdLen = pHeader->keyValueData.byteLength;
ktx_uint8_t* pKvd;
@@ -850,9 +940,30 @@ ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
} else {
stream->skip(stream, pHeader->keyValueData.byteLength);
}
+ } else if (pHeader->keyValueData.byteOffset != 0) {
+ // Non-zero KVD byteOffset with zero byteLength
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
}
if (pHeader->supercompressionGlobalData.byteLength > 0) {
+ switch (This->supercompressionScheme) {
+ case KTX_SS_BASIS_LZ:
+ break;
+ case KTX_SS_NONE:
+ case KTX_SS_ZSTD:
+ case KTX_SS_ZLIB:
+ // In these cases SGD is not allowed
+ result = KTX_FILE_DATA_ERROR;
+ break;
+ default:
+ // We don't support other supercompression schemes
+ result = KTX_UNSUPPORTED_FEATURE;
+ break;
+ }
+ if (result != KTX_SUCCESS)
+ goto cleanup;
+
// There could be padding here so seek to the next item.
(void)stream->setpos(stream,
pHeader->supercompressionGlobalData.byteOffset);
@@ -871,6 +982,14 @@ ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
if (result != KTX_SUCCESS)
goto cleanup;
+ } else if (pHeader->supercompressionGlobalData.byteOffset != 0) {
+ // Non-zero SGD byteOffset with zero byteLength
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
+ } else if (This->supercompressionScheme == KTX_SS_BASIS_LZ) {
+ // SGD is required for BasisLZ
+ result = KTX_FILE_DATA_ERROR;
+ goto cleanup;
}
// Calculate size of the image data. Level 0 is the last level in the data.
@@ -982,6 +1101,9 @@ ktxTexture2_constructFromStdioStream(ktxTexture2* This, FILE* stdioStream,
* @~English
* @brief Construct a ktxTexture from a named KTX file.
*
+ * The file name must be encoded in utf-8. On Windows convert unicode names
+ * to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.
+ *
* See ktxTextureInt_constructFromStream for details.
*
* @param[in] This pointer to a ktxTextureInt-sized block of memory to
@@ -1008,7 +1130,7 @@ ktxTexture2_constructFromNamedFile(ktxTexture2* This,
if (This == NULL || filename == NULL)
return KTX_INVALID_VALUE;
- file = fopen(filename, "rb");
+ file = ktxFOpenUTF8(filename, "rb");
if (!file)
return KTX_FILE_OPEN_FAILED;
@@ -1259,6 +1381,9 @@ ktxTexture2_CreateFromStdioStream(FILE* stdioStream,
* The address of a newly created ktxTexture2 reflecting the contents of the
* file is written to the location pointed at by @p newTex.
*
+ * The file name must be encoded in utf-8. On Windows convert unicode names
+ * to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.
+ *
* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
* will minimize memory usage by allowing, for example, loading the images
@@ -1581,7 +1706,7 @@ ktxTexture2_calcPostInflationLevelAlignment(ktxTexture2* This)
// Should actually work for none supercompressed but don't want to
// encourage use of it.
- assert(This->supercompressionScheme >= KTX_SS_ZSTD);
+ assert(This->supercompressionScheme != KTX_SS_NONE && This->supercompressionScheme != KTX_SS_BASIS_LZ);
if (This->vkFormat != VK_FORMAT_UNDEFINED)
alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);
@@ -1834,15 +1959,17 @@ ktxTexture2_NeedsTranscoding(ktxTexture2* This)
/**
* @memberof ktxTexture2
* @~English
- * @brief Return the total size in bytes of the uncompressed data of a ktxTexture2.
- *
- * If supercompressionScheme == KTX_SS_NONE or
- * KTX_SS_BASIS_LZ, returns the value of @c This->dataSize
- * else if supercompressionScheme == KTX_SS_ZSTD, it returns the
- * sum of the uncompressed sizes of each mip level plus space for the level padding. With no
- * supercompression the data size and uncompressed data size are the same. For Basis
- * supercompression the uncompressed size cannot be known until the data is transcoded
- * so the compressed size is returned.
+ * @brief Return the total size in bytes of the uncompressed data of a
+ * ktxTexture2.
+ *
+ * If supercompressionScheme == @c KTX_SS_NONE or
+ * @c KTX_SS_BASIS_LZ, returns the value of @c This->dataSize
+ * else if supercompressionScheme == @c KTX_SS_ZSTD or @c KTX_SS_ZLIB, it
+ * returns the sum of the uncompressed sizes of each mip level plus space for
+ * the level padding. With no supercompression the data size and uncompressed
+ * data size are the same. For Basis supercompression the uncompressed size
+ * cannot be known until the data is transcoded so the compressed size is
+ * returned.
*
* @param[in] This pointer to the ktxTexture1 object of interest.
*/
@@ -1854,6 +1981,7 @@ ktxTexture2_GetDataSizeUncompressed(ktxTexture2* This)
case KTX_SS_NONE:
return This->dataSize;
case KTX_SS_ZSTD:
+ case KTX_SS_ZLIB:
{
ktx_size_t uncompressedSize = 0;
ktx_uint32_t uncompressedLevelAlignment;
@@ -1984,7 +2112,7 @@ ktxTexture2_IterateLevels(ktxTexture2* This, PFNKTXITERCB iterCb, void* userdata
*
* This operates similarly to ktxTexture_IterateLevelFaces() except that it
* loads the images from the ktxTexture2's source to a temporary buffer
- * while iterating. If supercompressionScheme == KTX_SS_ZSTD,
+ * while iterating. If supercompressionScheme == KTX_SS_ZSTD or KTX_SS_ZLIB,
* it will inflate the data before passing it to the callback. The callback function
* must copy the image data if it wishes to preserve it as the temporary buffer
* is reused for each level and is freed when this function exits.
@@ -1992,8 +2120,8 @@ ktxTexture2_IterateLevels(ktxTexture2* This, PFNKTXITERCB iterCb, void* userdata
* This function is helpful for reducing memory usage when uploading the data
* to a graphics API.
*
- * Intended for use only when supercompressionScheme == SUPERCOMPRESSION_NONE
- * or SUPERCOMPRESSION_ZSTD. As there is no access to the ktxTexture's data on
+ * Intended for use only when supercompressionScheme == KTX_SS_NONE,
+ * KTX_SS_ZSTD or KTX_SS_ZLIB. As there is no access to the ktxTexture's data on
* conclusion of this function, destroying the texture on completion is recommended.
*
* @param[in] This pointer to the ktxTexture2 object of interest.
@@ -2013,8 +2141,9 @@ ktxTexture2_IterateLevels(ktxTexture2* This, PFNKTXITERCB iterCb, void* userdata
* this ktxTexture2's images have already
* been loaded.
* @exception KTX_INVALID_OPERATION
- * supercompressionScheme != SUPERCOMPRESSION_NONE.
- * and supercompressionScheme != SUPERCOMPRESSION_ZSTD.
+ * supercompressionScheme != KTX_SS_NONE,
+ * supercompressionScheme != KTX_SS_ZSTD, and
+ * supercompressionScheme != KTX_SS_ZLIB.
* @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.
* @exception KTX_OUT_OF_MEMORY not enough memory to allocate a block to
* hold the base level image.
@@ -2040,7 +2169,8 @@ ktxTexture2_IterateLoadLevelFaces(ktxTexture2* This, PFNKTXITERCB iterCb,
return KTX_INVALID_OPERATION;
if (This->supercompressionScheme != KTX_SS_NONE &&
- This->supercompressionScheme != KTX_SS_ZSTD)
+ This->supercompressionScheme != KTX_SS_ZSTD &&
+ This->supercompressionScheme != KTX_SS_ZLIB)
return KTX_INVALID_OPERATION;
if (iterCb == NULL)
@@ -2057,14 +2187,16 @@ ktxTexture2_IterateLoadLevelFaces(ktxTexture2* This, PFNKTXITERCB iterCb,
dataBuf = malloc(dataSize);
if (!dataBuf)
return KTX_OUT_OF_MEMORY;
- if (This->supercompressionScheme == KTX_SS_ZSTD) {
+ if (This->supercompressionScheme == KTX_SS_ZSTD || This->supercompressionScheme == KTX_SS_ZLIB) {
uncompressedDataSize = levelIndex[0].uncompressedByteLength;
uncompressedDataBuf = malloc(uncompressedDataSize);
if (!uncompressedDataBuf) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
- dctx = ZSTD_createDCtx();
+ if (This->supercompressionScheme == KTX_SS_ZSTD) {
+ dctx = ZSTD_createDCtx();
+ }
pData = uncompressedDataBuf;
} else {
pData = dataBuf;
@@ -2107,21 +2239,34 @@ ktxTexture2_IterateLoadLevelFaces(ktxTexture2* This, PFNKTXITERCB iterCb,
ZSTD_ErrorCode error = ZSTD_getErrorCode(levelSize);
switch(error) {
case ZSTD_error_dstSize_tooSmall:
- return KTX_INVALID_VALUE; // inflatedDataCapacity too small.
+ return KTX_DECOMPRESS_LENGTH_ERROR; // inflatedDataCapacity too small.
+ case ZSTD_error_checksum_wrong:
+ return KTX_DECOMPRESS_CHECKSUM_ERROR;
case ZSTD_error_memory_allocation:
return KTX_OUT_OF_MEMORY;
default:
return KTX_FILE_DATA_ERROR;
}
}
+
// We don't fix up the texture's dataSize, levelIndex or
// _requiredAlignment because after this function completes there
// is no way to get at the texture's data.
//nindex[level].byteOffset = levelOffset;
//nindex[level].uncompressedByteLength = nindex[level].byteLength =
//levelByteLength;
+ } else if (This->supercompressionScheme == KTX_SS_ZLIB) {
+ result = ktxUncompressZLIBInt(uncompressedDataBuf,
+ &uncompressedDataSize,
+ dataBuf,
+ levelSize);
+ if (result != KTX_SUCCESS)
+ return result;
}
+ if (levelIndex[level].uncompressedByteLength != levelSize)
+ return KTX_DECOMPRESS_LENGTH_ERROR;
+
#if IS_BIG_ENDIAN
switch (prtctd->_typeSize) {
case 2:
@@ -2188,16 +2333,23 @@ KTX_error_code
ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
ktx_uint8_t* pInflatedData,
ktx_size_t inflatedDataCapacity);
+
+KTX_error_code
+ktxTexture2_inflateZLIBInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
+ ktx_uint8_t* pInflatedData,
+ ktx_size_t inflatedDataCapacity);
+
/**
* @memberof ktxTexture2
* @~English
* @brief Load all the image data from the ktxTexture2's source.
*
- * The data will be inflated if supercompressionScheme == SUPERCOMPRESSION_ZSTD.
+ * The data will be inflated if supercompressionScheme == @c KTX_SS_ZSTD or
+ * @c KTX_SS_ZLIB.
* The data is loaded into the provided buffer or to an internally allocated
* buffer, if @p pBuffer is @c NULL. Callers providing their own buffer must
* ensure the buffer large enough to hold the inflated data for files deflated
- * with Zstd. See ktxTexture2_GetDataSizeUncompressed().
+ * with Zstd or ZLIB. See ktxTexture2\_GetDataSizeUncompressed().
*
* The texture's levelIndex, dataSize, DFD and supercompressionScheme will
* all be updated after successful inflation to reflect the inflated data.
@@ -2248,7 +2400,7 @@ ktxTexture2_LoadImageData(ktxTexture2* This,
pDest = pBuffer;
}
- if (This->supercompressionScheme == KTX_SS_ZSTD) {
+ if (This->supercompressionScheme == KTX_SS_ZSTD || This->supercompressionScheme == KTX_SS_ZLIB) {
// Create buffer to hold deflated data.
pDeflatedData = malloc(This->dataSize);
if (pDeflatedData == NULL)
@@ -2271,10 +2423,15 @@ ktxTexture2_LoadImageData(ktxTexture2* This,
if (result != KTX_SUCCESS)
return result;
- if (This->supercompressionScheme == KTX_SS_ZSTD) {
+ if (This->supercompressionScheme == KTX_SS_ZSTD || This->supercompressionScheme == KTX_SS_ZLIB) {
assert(pDeflatedData != NULL);
- result = ktxTexture2_inflateZstdInt(This, pDeflatedData, pDest,
- inflatedDataCapacity);
+ if (This->supercompressionScheme == KTX_SS_ZSTD) {
+ result = ktxTexture2_inflateZstdInt(This, pDeflatedData, pDest,
+ inflatedDataCapacity);
+ } else if (This->supercompressionScheme == KTX_SS_ZLIB) {
+ result = ktxTexture2_inflateZLIBInt(This, pDeflatedData, pDest,
+ inflatedDataCapacity);
+ }
free(pDeflatedData);
if (result != KTX_SUCCESS) {
if (pBuffer == NULL) {
@@ -2388,13 +2545,19 @@ ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
ZSTD_ErrorCode error = ZSTD_getErrorCode(levelByteLength);
switch(error) {
case ZSTD_error_dstSize_tooSmall:
- return KTX_INVALID_VALUE; // inflatedDataCapacity too small.
+ return KTX_DECOMPRESS_LENGTH_ERROR; // inflatedDataCapacity too small.
+ case ZSTD_error_checksum_wrong:
+ return KTX_DECOMPRESS_CHECKSUM_ERROR;
case ZSTD_error_memory_allocation:
return KTX_OUT_OF_MEMORY;
default:
return KTX_FILE_DATA_ERROR;
}
}
+
+ if (This->_private->_levelIndex[level].uncompressedByteLength != levelByteLength)
+ return KTX_DECOMPRESS_LENGTH_ERROR;
+
nindex[level].byteOffset = levelOffset;
nindex[level].uncompressedByteLength = nindex[level].byteLength =
levelByteLength;
@@ -2421,6 +2584,89 @@ ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
return KTX_SUCCESS;
}
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Inflate the data in a ktxTexture2 object using miniz (ZLIB).
+ *
+ * The texture's levelIndex, dataSize, DFD and supercompressionScheme will
+ * all be updated after successful inflation to reflect the inflated data.
+ *
+ * @param[in] This pointer to the ktxTexture2 object of interest.
+ * @param[in] pDeflatedData pointer to a buffer containing the deflated
+ * data of the entire texture.
+ * @param[in,out] pInflatedData pointer to a buffer in which to write the
+ * inflated data.
+ * @param[in] inflatedDataCapacity capacity of the buffer pointed at by
+ * @p pInflatedData.
+ */
+KTX_error_code
+ktxTexture2_inflateZLIBInt(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);
+ uint64_t levelOffset = 0;
+ ktxLevelIndexEntry* cindex = This->_private->_levelIndex;
+ ktxLevelIndexEntry* nindex;
+ ktx_uint32_t uncompressedLevelAlignment;
+
+ if (pDeflatedData == NULL)
+ return KTX_INVALID_VALUE;
+
+ if (pInflatedData == NULL)
+ return KTX_INVALID_VALUE;
+
+ if (This->supercompressionScheme != KTX_SS_ZLIB)
+ return KTX_INVALID_OPERATION;
+
+ nindex = malloc(levelIndexByteLength);
+ if (nindex == NULL)
+ return KTX_OUT_OF_MEMORY;
+
+ uncompressedLevelAlignment =
+ ktxTexture2_calcPostInflationLevelAlignment(This);
+
+ ktx_size_t inflatedByteLength = 0;
+ for (int32_t level = This->numLevels - 1; level >= 0; level--) {
+ size_t levelByteLength = inflatedDataCapacity;
+ KTX_error_code result = ktxUncompressZLIBInt(pInflatedData + levelOffset,
+ &levelByteLength,
+ &pDeflatedData[cindex[level].byteOffset],
+ cindex[level].byteLength);
+ if (result != KTX_SUCCESS)
+ return result;
+
+ if (This->_private->_levelIndex[level].uncompressedByteLength != levelByteLength)
+ return KTX_DECOMPRESS_LENGTH_ERROR;
+
+ nindex[level].byteOffset = levelOffset;
+ nindex[level].uncompressedByteLength = nindex[level].byteLength =
+ levelByteLength;
+ ktx_size_t paddedLevelByteLength
+ = _KTX_PADN(uncompressedLevelAlignment, levelByteLength);
+ inflatedByteLength += paddedLevelByteLength;
+ levelOffset += paddedLevelByteLength;
+ inflatedDataCapacity -= paddedLevelByteLength;
+ }
+
+ // Now modify the texture.
+
+ This->dataSize = inflatedByteLength;
+ This->supercompressionScheme = KTX_SS_NONE;
+ memcpy(cindex, nindex, levelIndexByteLength); // Update level index
+ free(nindex);
+ This->_private->_requiredLevelAlignment = uncompressedLevelAlignment;
+ // Set bytesPlane as we're now sized.
+ uint32_t* bdb = This->pDfd + 1;
+ // blockSizeInBits was set to the inflated size on file load.
+ bdb[KHR_DF_WORD_BYTESPLANE0] = prtctd->_formatSize.blockSizeInBits / 8;
+
+ return KTX_SUCCESS;
+}
+
#if !KTX_FEATURE_WRITE
/*