/* -*- tab-width: 4; -*- */1/* vi: set sw=2 ts=4 expandtab: */23/*4* Copyright 2019-2020 The Khronos Group Inc.5* SPDX-License-Identifier: Apache-2.06*/78/**9* @internal10* @file11* @~English12*13* @brief ktxTexture2 implementation. Support for KTX2 format.14*15* @author Mark Callow, www.edgewise-consulting.com16*/1718#if defined(_WIN32)19#define _CRT_SECURE_NO_WARNINGS20#endif2122#include <assert.h>23#include <stdlib.h>24#include <string.h>25#include <math.h>26#include <zstd.h>27#include <zstd_errors.h>28#include <KHR/khr_df.h>2930#include "dfdutils/dfd.h"31#include "ktx.h"32#include "ktxint.h"33#include "filestream.h"34#include "memstream.h"35#include "texture2.h"36#include "unused.h"3738// 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 04142extern uint32_t vkFormatTypeSize(VkFormat format);43extern bool isProhibitedFormat(VkFormat format);4445struct ktxTexture_vtbl ktxTexture2_vtbl;46struct ktxTexture_vtblInt ktxTexture2_vtblInt;4748#if !defined(BITFIELD_ORDER_FROM_MSB)49// Most compilers, including all those tested so far, including clang, gcc50// and msvc, order bitfields from the lsb so these struct declarations work.51// Could this be because I've only tested on little-endian machines?52// These are preferred as they are much easier to manually initialize53// and verify.54struct sampleType {55uint32_t bitOffset: 16;56uint32_t bitLength: 8;57uint32_t channelType: 8; // Includes qualifiers58uint32_t samplePosition0: 8;59uint32_t samplePosition1: 8;60uint32_t samplePosition2: 8;61uint32_t samplePosition3: 8;62uint32_t lower;63uint32_t upper;64};6566struct BDFD {67uint32_t vendorId: 17;68uint32_t descriptorType: 15;69uint32_t versionNumber: 16;70uint32_t descriptorBlockSize: 16;71uint32_t model: 8;72uint32_t primaries: 8;73uint32_t transfer: 8;74uint32_t flags: 8;75uint32_t texelBlockDimension0: 8;76uint32_t texelBlockDimension1: 8;77uint32_t texelBlockDimension2: 8;78uint32_t texelBlockDimension3: 8;79uint32_t bytesPlane0: 8;80uint32_t bytesPlane1: 8;81uint32_t bytesPlane2: 8;82uint32_t bytesPlane3: 8;83uint32_t bytesPlane4: 8;84uint32_t bytesPlane5: 8;85uint32_t bytesPlane6: 8;86uint32_t bytesPlane7: 8;87struct sampleType samples[6];88};8990struct BDFD e5b9g9r9_ufloat_comparator = {91.vendorId = 0,92.descriptorType = 0,93.versionNumber = 2,94.descriptorBlockSize = sizeof(struct BDFD),95.model = KHR_DF_MODEL_RGBSDA,96.primaries = KHR_DF_PRIMARIES_BT709,97.transfer = KHR_DF_TRANSFER_LINEAR,98.flags = KHR_DF_FLAG_ALPHA_STRAIGHT,99.texelBlockDimension0 = 0,100.texelBlockDimension1 = 0,101.texelBlockDimension2 = 0,102.texelBlockDimension3 = 0,103.bytesPlane0 = 4,104.bytesPlane1 = 0,105.bytesPlane2 = 0,106.bytesPlane3 = 0,107.bytesPlane4 = 0,108.bytesPlane5 = 0,109.bytesPlane6 = 0,110.bytesPlane7 = 0,111// gcc likes this way. It does not like, e.g.,112// .samples[0].bitOffset = 0, etc. which is accepted by both clang & msvc.113// I find the standards docs impenetrable so I don't know which is correct.114.samples[0] = {115.bitOffset = 0,116.bitLength = 8,117.channelType = KHR_DF_CHANNEL_RGBSDA_RED,118.samplePosition0 = 0,119.samplePosition1 = 0,120.samplePosition2 = 0,121.samplePosition3 = 0,122.lower = 0,123.upper = 8448,124},125.samples[1] = {126.bitOffset = 27,127.bitLength = 4,128.channelType = KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT,129.samplePosition0 = 0,130.samplePosition1 = 0,131.samplePosition2 = 0,132.samplePosition3 = 0,133.lower = 15,134.upper = 31,135},136.samples[2] = {137.bitOffset = 9,138.bitLength = 8,139.channelType = KHR_DF_CHANNEL_RGBSDA_GREEN,140.samplePosition0 = 0,141.samplePosition1 = 0,142.samplePosition2 = 0,143.samplePosition3 = 0,144.lower = 0,145.upper = 8448,146},147.samples[3] = {148.bitOffset = 27,149.bitLength = 4,150.channelType = KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT,151.samplePosition0 = 0,152.samplePosition1 = 0,153.samplePosition2 = 0,154.samplePosition3 = 0,155.lower = 15,156.upper = 31,157},158.samples[4] = {159.bitOffset = 18,160.bitLength = 8,161.channelType = KHR_DF_CHANNEL_RGBSDA_BLUE,162.samplePosition0 = 0,163.samplePosition1 = 0,164.samplePosition2 = 0,165.samplePosition3 = 0,166.lower = 0,167.upper = 8448,168},169.samples[5] = {170.bitOffset = 27,171.bitLength = 4,172.channelType = KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT,173.samplePosition0 = 0,174.samplePosition1 = 0,175.samplePosition2 = 0,176.samplePosition3 = 0,177.lower = 15,178.upper = 31,179}180};181#else182// For compilers which order bitfields from the msb rather than lsb.183#define shift(x,val) ((val) << KHR_DF_SHIFT_ ## x)184#define sampleshift(x,val) ((val) << KHR_DF_SAMPLESHIFT_ ## x)185#define e5b9g9r9_bdbwordcount KHR_DFDSIZEWORDS(6)186ktx_uint32_t e5b9g9r9_ufloat_comparator[e5b9g9r9_bdbwordcount] = {1870, // descriptorType & vendorId188shift(DESCRIPTORBLOCKSIZE, e5b9g9r9_bdbwordcount * sizeof(ktx_uint32_t)) | shift(VERSIONNUMBER, 2),189// N.B. Allow various values of primaries, transfer & flags190shift(FLAGS, KHR_DF_FLAG_ALPHA_STRAIGHT) | shift(TRANSFER, KHR_DF_TRANSFER_LINEAR) | shift(PRIMARIES, KHR_DF_PRIMARIES_BT709) | shift(MODEL, KHR_DF_MODEL_RGBSDA),1910, // texelBlockDimension3~0192shift(BYTESPLANE0, 4), // All other bytesPlane fields are 0.1930, // bytesPlane7~4194sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 0),1950, // samplePosition3~01960, // sampleLower1978448, // sampleUpper198sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),1990, // samplePosition3~020015, // sampleLower20131, // sampleUpper202sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 9),2030, // samplePosition3~02040, // sampleLower2058448, // sampleUpper206sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),2070, // samplePosition3~020815, // sampleLower20931, // sampleUpper210sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 18),2110, // samplePosition3~02120, // sampleLower2138448, // sampleUpper214sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),2150, // samplePosition3~021615, // sampleLower21731, // sampleUpper218};219#endif220221/* Helper constant:222Minimal size of basic descriptor block to safely read its size */223#define KHR_DFD_SIZEFOR_DESCRIPTORBLOCKSIZE \224((KHR_DF_WORD_DESCRIPTORBLOCKSIZE + 1) * sizeof(uint32_t))225226/**227* @private228* @~English229* @brief Initialize a ktxFormatSize object from the info in a DFD.230*231* This is used instead of referring to the DFD directly so code dealing232* with format info can be common to KTX 1 & 2.233*234* @param[in] This pointer the ktxFormatSize to initialize.235* @param[in] pDFD pointer to the DFD whose data to use.236*237* @return KTX_TRUE on success, otherwise KTX_FALSE.238*/239bool240ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd)241{242uint32_t* pBdb = pDfd + 1;243// pDfd[0] contains totalSize in bytes, check if it has at least244// KHR_DFD_SIZEFOR_DESCRIPTORBLOCKSIZE bytes245if (pDfd[0] < KHR_DFD_SIZEFOR_DESCRIPTORBLOCKSIZE || *pBdb != 0) {246// Either decriptorType or vendorId is not 0247return false;248}249// Iterate through all block descriptors and check if sum of their sizes250// is equal to the totalSize in pDfd[0]251uint32_t descriptorSize = pDfd[0] - sizeof(uint32_t);252while(descriptorSize > KHR_DFD_SIZEFOR_DESCRIPTORBLOCKSIZE) {253uint32_t descriptorBlockSize = KHR_DFDVAL(pBdb, DESCRIPTORBLOCKSIZE);254if (descriptorBlockSize <= descriptorSize) {255descriptorSize -= descriptorBlockSize;256pBdb += descriptorBlockSize / sizeof(uint32_t);257} else {258break;259}260}261if (descriptorSize != 0) {262return false;263}264265// reset pBdb pointer to the first block descriptor266pBdb = pDfd + 1;267268// Check the DFD is of the expected version.269if (KHR_DFDVAL(pBdb, VERSIONNUMBER) != KHR_DF_VERSIONNUMBER_1_3) {270return false;271}272273// DFD has supported type and version. Process it.274This->blockWidth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION0) + 1;275This->blockHeight = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION1) + 1;276This->blockDepth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION2) + 1;277if (KHR_DFDVAL(pBdb, BYTESPLANE0) == 0) {278// The DFD uses the deprecated way of indicating a supercompressed279// texture. Reconstruct the original values.280reconstructDFDBytesPlanesFromSamples(pDfd);281}282This->blockSizeInBits = KHR_DFDVAL(pBdb, BYTESPLANE0) * 8;283// Account for ETC1S with possible second slice.284This->blockSizeInBits += KHR_DFDVAL(pBdb, BYTESPLANE1) * 8;285This->paletteSizeInBits = 0; // No paletted formats in ktx v2.286This->flags = 0;287This->minBlocksX = This->minBlocksY = 1;288if (KHR_DFDVAL(pBdb, MODEL) >= KHR_DF_MODEL_DXT1A) {289// A block compressed format. Entire block is a single sample.290This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT;291if (KHR_DFDVAL(pBdb, MODEL) == KHR_DF_MODEL_ETC1S) {292// Special case the only multi-plane format we handle.293This->blockSizeInBits += KHR_DFDVAL(pBdb, BYTESPLANE1) * 8;294}295if (KHR_DFDVAL(pBdb, MODEL) == KHR_DF_MODEL_PVRTC) {296This->minBlocksX = This->minBlocksY = 2;297}298} else {299// An uncompressed format.300301// Special case depth & depth stencil formats302if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_DEPTH) {303if (KHR_DFDSAMPLECOUNT(pBdb) == 1) {304This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;305} else if (KHR_DFDSAMPLECOUNT(pBdb) == 2) {306This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;307This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;308This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;309} else {310return false;311}312} else if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_STENCIL) {313This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;314} else if (KHR_DFDSAMPLECOUNT(pBdb) == 6315#if !defined(BITFIELD_ORDER_FROM_MSB)316&& !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))) {317#else318&& !memcmp(&e5b9g9r9_ufloat_comparator[KHR_DF_WORD_TEXELBLOCKDIMENSION0], &pBdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0], sizeof(e5b9g9r9_ufloat_comparator)-(KHR_DF_WORD_TEXELBLOCKDIMENSION0)*sizeof(uint32_t))) {319#endif320// Special case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 as interpretDFD321// only handles "simple formats", i.e. where channels are described322// in contiguous bits.323This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;324} else {325InterpretedDFDChannel rgba[4];326uint32_t wordBytes;327enum InterpretDFDResult result;328329result = interpretDFD(pDfd, &rgba[0], &rgba[1], &rgba[2], &rgba[3],330&wordBytes);331if (result >= i_UNSUPPORTED_ERROR_BIT)332return false;333if (result & i_PACKED_FORMAT_BIT)334This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;335if (result & i_COMPRESSED_FORMAT_BIT)336This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT;337if (result & i_YUVSDA_FORMAT_BIT)338This->flags |= KTX_FORMAT_SIZE_YUVSDA_BIT;339}340}341return true;342}343344/**345* @private346* @~English347* @brief Create a DFD for a VkFormat.348*349* This KTX-specific function adds support for combined depth stencil formats350* which are not supported by @e dfdutils' @c vk2dfd function because they351* are not seen outside a Vulkan device. KTX has its own definitions for352* these that enable uploading, with some effort.353*354* @param[in] vkFormat the format for which to create a DFD.355*/356static uint32_t*357ktxVk2dfd(ktx_uint32_t vkFormat)358{359return vk2dfd(vkFormat);360}361362/**363* @memberof ktxTexture2 @private364* @~English365* @brief Do the part of ktxTexture2 construction that is common to366* new textures and those constructed from a stream.367*368* @param[in] This pointer to a ktxTexture2-sized block of memory to369* initialize.370* @param[in] numLevels the number of levels the texture must have.371*372* @return KTX_SUCCESS on success, other KTX_* enum values on error.373* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.374*/375static KTX_error_code376ktxTexture2_constructCommon(ktxTexture2* This, ktx_uint32_t numLevels)377{378assert(This != NULL);379ktx_size_t privateSize;380381This->classId = ktxTexture2_c;382This->vtbl = &ktxTexture2_vtbl;383This->_protected->_vtbl = ktxTexture2_vtblInt;384privateSize = sizeof(ktxTexture2_private)385+ sizeof(ktxLevelIndexEntry) * (numLevels - 1);386This->_private = (ktxTexture2_private*)malloc(privateSize);387if (This->_private == NULL) {388return KTX_OUT_OF_MEMORY;389}390memset(This->_private, 0, privateSize);391return KTX_SUCCESS;392}393394/*395* In hindsight this function should have been `#if KTX_FEATURE_WRITE`.396* In the interest of not breaking an app that may be using this via397* `ktxTexture2_Create` in `libktx_read` we won't change it.398*/399/**400* @memberof ktxTexture2 @private401* @~English402* @brief Construct a new, empty, ktxTexture2.403*404* @param[in] This pointer to a ktxTexture2-sized block of memory to405* initialize.406* @param[in] createInfo pointer to a ktxTextureCreateInfo struct with407* information describing the texture.408* @param[in] storageAllocation409* enum indicating whether or not to allocate storage410* for the texture images.411* @return KTX_SUCCESS on success, other KTX_* enum values on error.412* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture or image data.413* @exception KTX_UNSUPPORTED_TEXTURE_TYPE414* The request VkFormat is one of the415* prohibited formats or is otherwise416* unsupported.417*/418static KTX_error_code419ktxTexture2_construct(ktxTexture2* This,420const ktxTextureCreateInfo* const createInfo,421ktxTextureCreateStorageEnum storageAllocation)422{423ktxFormatSize formatSize;424KTX_error_code result;425426memset(This, 0, sizeof(*This));427428if (createInfo->vkFormat != VK_FORMAT_UNDEFINED) {429if (isProhibitedFormat(createInfo->vkFormat))430return KTX_UNSUPPORTED_TEXTURE_TYPE;431This->pDfd = ktxVk2dfd(createInfo->vkFormat);432if (!This->pDfd)433return KTX_INVALID_VALUE; // Format is unknown or unsupported.434435#ifdef _DEBUG436// If this fires, an unsupported format or incorrect DFD437// has crept into vk2dfd.438assert(ktxFormatSize_initFromDfd(&formatSize, This->pDfd));439#else440(void)ktxFormatSize_initFromDfd(&formatSize, This->pDfd);441#endif442443} else {444// TODO: Validate createInfo->pDfd.445This->pDfd = (ktx_uint32_t*)malloc(*createInfo->pDfd);446if (!This->pDfd)447return KTX_OUT_OF_MEMORY;448memcpy(This->pDfd, createInfo->pDfd, *createInfo->pDfd);449if (!ktxFormatSize_initFromDfd(&formatSize, This->pDfd)) {450result = KTX_UNSUPPORTED_TEXTURE_TYPE;451goto cleanup;452}453}454455result = ktxTexture_construct(ktxTexture(This), createInfo, &formatSize);456457if (result != KTX_SUCCESS)458return result;459result = ktxTexture2_constructCommon(This, createInfo->numLevels);460if (result != KTX_SUCCESS)461goto cleanup;;462463This->vkFormat = createInfo->vkFormat;464465// The typeSize cannot be reconstructed just from the DFD as the BDFD466// does not capture the packing expressed by the [m]PACK[n] layout467// information in the VkFormat, so we calculate the typeSize directly468// from the vkFormat469This->_protected->_typeSize = vkFormatTypeSize(createInfo->vkFormat);470471This->supercompressionScheme = KTX_SS_NONE;472473This->_private->_requiredLevelAlignment474= ktxTexture2_calcRequiredLevelAlignment(This);475476// Create levelIndex. Offsets are from start of the KTX2 stream.477ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;478479This->_private->_firstLevelFileOffset = 0;480481for (ktx_uint32_t level = 0; level < This->numLevels; level++) {482levelIndex[level].uncompressedByteLength =483ktxTexture_calcLevelSize(ktxTexture(This), level,484KTX_FORMAT_VERSION_TWO);485levelIndex[level].byteLength =486levelIndex[level].uncompressedByteLength;487levelIndex[level].byteOffset =488ktxTexture_calcLevelOffset(ktxTexture(This), level);489}490491// Allocate storage, if requested.492if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) {493This->dataSize494= ktxTexture_calcDataSizeTexture(ktxTexture(This));495This->pData = malloc(This->dataSize);496if (This->pData == NULL) {497result = KTX_OUT_OF_MEMORY;498goto cleanup;499}500}501return result;502503cleanup:504ktxTexture2_destruct(This);505return result;506}507508/**509* @memberof ktxTexture2 @private510* @~English511* @brief Construct a ktxTexture by copying a source ktxTexture.512*513* @param[in] This pointer to a ktxTexture2-sized block of memory to514* initialize.515* @param[in] orig pointer to the source texture to copy.516*517* @return KTX_SUCCESS on success, other KTX_* enum values on error.518*519* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.520*/521KTX_error_code522ktxTexture2_constructCopy(ktxTexture2* This, ktxTexture2* orig)523{524KTX_error_code result;525526memcpy(This, orig, sizeof(ktxTexture2));527// Zero all the pointers to make error handling easier528This->_protected = NULL;529This->_private = NULL;530This->pDfd = NULL;531This->kvData = NULL;532This->kvDataHead = NULL;533This->pData = NULL;534535This->_protected =536(ktxTexture_protected*)malloc(sizeof(ktxTexture_protected));537if (!This->_protected)538return KTX_OUT_OF_MEMORY;539// Must come before memcpy of _protected so as to close an active stream.540if (!orig->pData && ktxTexture_isActiveStream((ktxTexture*)orig))541ktxTexture2_LoadImageData(orig, NULL, 0);542memcpy(This->_protected, orig->_protected, sizeof(ktxTexture_protected));543544ktx_size_t privateSize = sizeof(ktxTexture2_private)545+ sizeof(ktxLevelIndexEntry) * (orig->numLevels - 1);546This->_private = (ktxTexture2_private*)malloc(privateSize);547if (This->_private == NULL) {548result = KTX_OUT_OF_MEMORY;549goto cleanup;550}551memcpy(This->_private, orig->_private, privateSize);552if (orig->_private->_sgdByteLength > 0) {553This->_private->_supercompressionGlobalData554= (ktx_uint8_t*)malloc(orig->_private->_sgdByteLength);555if (!This->_private->_supercompressionGlobalData) {556result = KTX_OUT_OF_MEMORY;557goto cleanup;558}559memcpy(This->_private->_supercompressionGlobalData,560orig->_private->_supercompressionGlobalData,561orig->_private->_sgdByteLength);562}563564This->pDfd = (ktx_uint32_t*)malloc(*orig->pDfd);565if (!This->pDfd) {566result = KTX_OUT_OF_MEMORY;567goto cleanup;568}569memcpy(This->pDfd, orig->pDfd, *orig->pDfd);570571if (orig->kvDataHead) {572ktxHashList_ConstructCopy(&This->kvDataHead, orig->kvDataHead);573} else if (orig->kvData) {574This->kvData = (ktx_uint8_t*)malloc(orig->kvDataLen);575if (!This->kvData) {576result = KTX_OUT_OF_MEMORY;577goto cleanup;578}579memcpy(This->kvData, orig->kvData, orig->kvDataLen);580}581582// Can't share the image data as the data pointer is exposed in the583// ktxTexture2 structure. Changing it to a ref-counted pointer would584// break code. Maybe that's okay as we're still pre-release. But,585// since this constructor will be mostly be used when transcoding586// supercompressed images, it is probably not too big a deal to make587// a copy of the data.588This->pData = (ktx_uint8_t*)malloc(This->dataSize);589if (This->pData == NULL) {590result = KTX_OUT_OF_MEMORY;591goto cleanup;592}593memcpy(This->pData, orig->pData, orig->dataSize);594return KTX_SUCCESS;595596cleanup:597if (This->_protected) free(This->_protected);598if (This->_private) {599if (This->_private->_supercompressionGlobalData)600free(This->_private->_supercompressionGlobalData);601free(This->_private);602}603if (This->pDfd) free (This->pDfd);604if (This->kvDataHead) ktxHashList_Destruct(&This->kvDataHead);605606return result;607}608609bool isSrgbFormat(VkFormat format);610bool isNotSrgbFormatButHasSrgbVariant(VkFormat format);611612/**613* @memberof ktxTexture2 @private614* @~English615* @brief Construct a ktxTexture from a ktxStream reading from a KTX source.616*617* The KTX header, which must have been read prior to calling this, is passed618* to the function.619*620* The stream object is copied into the constructed ktxTexture2.621*622* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,623* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This624* will minimize memory usage by allowing, for example, loading the images625* directly from the source into a Vulkan staging buffer.626*627* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is628* provided solely to enable implementation of the @e libktx v1 API on top of629* ktxTexture.630*631* If either KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT or632* KTX_TEXTURE_CREATE_RAW_KVDATA_BIT is set then the ktxTexture's orientation633* fields will be set to defaults even if the KTX source contains634* KTXorientation metadata.635*636* @param[in] This pointer to a ktxTexture2-sized block of memory to637* initialize.638* @param[in] pStream pointer to the stream to read.639* @param[in] pHeader pointer to a KTX header that has already been read from640* the stream.641* @param[in] createFlags bitmask requesting specific actions during creation.642*643* @return KTX_SUCCESS on success, other KTX_* enum values on error.644*645* @exception KTX_FILE_DATA_ERROR646* Source data is inconsistent with the KTX647* specification.648* @exception KTX_FILE_READ_ERROR649* An error occurred while reading the source.650* @exception KTX_FILE_UNEXPECTED_EOF651* Not enough data in the source.652* @exception KTX_OUT_OF_MEMORY Not enough memory to load either the images or653* the key-value data.654* @exception KTX_UNKNOWN_FILE_FORMAT655* The source is not in KTX format.656* @exception KTX_UNSUPPORTED_TEXTURE_TYPE657* The source describes a texture type not658* supported by OpenGL or Vulkan, e.g, a 3D array.659*/660KTX_error_code661ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,662KTX_header2* pHeader,663ktxTextureCreateFlags createFlags)664{665ktxTexture2_private* private;666KTX_error_code result;667KTX_supplemental_info suppInfo;668ktxStream* stream;669struct BDFD* pBDFD;670ktx_size_t levelIndexSize;671672assert(pHeader != NULL && pStream != NULL);673674memset(This, 0, sizeof(*This));675result = ktxTexture_constructFromStream(ktxTexture(This), pStream,676createFlags);677if (result != KTX_SUCCESS)678return result;679680result = ktxCheckHeader2_(pHeader, &suppInfo);681if (result != KTX_SUCCESS)682goto cleanup;683// ktxCheckHeader2_ has done the max(1, levelCount) on pHeader->levelCount.684result = ktxTexture2_constructCommon(This, pHeader->levelCount);685if (result != KTX_SUCCESS)686goto cleanup;687private = This->_private;688689stream = ktxTexture2_getStream(This);690691/*692* Initialize from pHeader->info.693*/694This->vkFormat = pHeader->vkFormat;695This->supercompressionScheme = pHeader->supercompressionScheme;696697This->_protected->_typeSize = pHeader->typeSize;698// Can these be done by a ktxTexture_constructFromStream?699This->numDimensions = suppInfo.textureDimension;700This->baseWidth = pHeader->pixelWidth;701assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4);702switch (suppInfo.textureDimension) {703case 1:704This->baseHeight = This->baseDepth = 1;705break;706case 2:707This->baseHeight = pHeader->pixelHeight;708This->baseDepth = 1;709break;710case 3:711This->baseHeight = pHeader->pixelHeight;712This->baseDepth = pHeader->pixelDepth;713break;714}715if (pHeader->layerCount > 0) {716This->numLayers = pHeader->layerCount;717This->isArray = KTX_TRUE;718} else {719This->numLayers = 1;720This->isArray = KTX_FALSE;721}722This->numFaces = pHeader->faceCount;723if (pHeader->faceCount == 6)724This->isCubemap = KTX_TRUE;725else726This->isCubemap = KTX_FALSE;727// ktxCheckHeader2_ does the max(1, levelCount) and sets728// suppInfo.generateMipmaps when it was originally 0.729This->numLevels = pHeader->levelCount;730This->generateMipmaps = suppInfo.generateMipmaps;731732// Read level index733levelIndexSize = sizeof(ktxLevelIndexEntry) * This->numLevels;734result = stream->read(stream, &private->_levelIndex, levelIndexSize);735if (result != KTX_SUCCESS)736goto cleanup;737// Rebase index to start of data and save file offset.738private->_firstLevelFileOffset739= private->_levelIndex[This->numLevels-1].byteOffset;740for (ktx_uint32_t level = 0; level < This->numLevels; level++) {741private->_levelIndex[level].byteOffset742-= private->_firstLevelFileOffset;743if (This->supercompressionScheme == KTX_SS_NONE &&744private->_levelIndex[level].byteLength != private->_levelIndex[level].uncompressedByteLength) {745// For non-supercompressed files the levels must have matching byte lengths746result = KTX_FILE_DATA_ERROR;747}748}749if (result != KTX_SUCCESS)750goto cleanup;751752// Read DFD753if (pHeader->dataFormatDescriptor.byteOffset == 0 || pHeader->dataFormatDescriptor.byteLength < 16) {754// Missing or too small DFD755result = KTX_FILE_DATA_ERROR;756goto cleanup;757}758This->pDfd =759(ktx_uint32_t*)malloc(pHeader->dataFormatDescriptor.byteLength);760if (!This->pDfd) {761result = KTX_OUT_OF_MEMORY;762goto cleanup;763}764result = stream->read(stream, This->pDfd,765pHeader->dataFormatDescriptor.byteLength);766if (result != KTX_SUCCESS)767goto cleanup;768769if (pHeader->dataFormatDescriptor.byteLength != This->pDfd[0]) {770// DFD byteLength does not match dfdTotalSize771result = KTX_FILE_DATA_ERROR;772goto cleanup;773}774pBDFD = (struct BDFD*)(This->pDfd + 1);775if (pBDFD->descriptorBlockSize < 24 || (pBDFD->descriptorBlockSize - 24) % 16 != 0) {776// BDFD has invalid size777result = KTX_FILE_DATA_ERROR;778goto cleanup;779}780if (pBDFD->transfer > KHR_DF_TRANSFER_HLG_UNNORMALIZED_OETF) {781// Invalid transfer function782result = KTX_FILE_DATA_ERROR;783goto cleanup;784}785// No test for VK_FORMAT_UNDEFINED is needed here because:786// - any transfer function is allowed when vkFormat is UNDEFINED as with,787// e.g., some Basis Universal formats;788// - the following tests return false for VK_FORMAT_UNDEFINED.789if (isSrgbFormat(This->vkFormat) && pBDFD->transfer != KHR_DF_TRANSFER_SRGB) {790// Invalid transfer function791result = KTX_FILE_DATA_ERROR;792goto cleanup;793}794if (isNotSrgbFormatButHasSrgbVariant(This->vkFormat)795&& pBDFD->transfer == KHR_DF_TRANSFER_SRGB) {796// Invalid transfer function797result = KTX_FILE_DATA_ERROR;798goto cleanup;799}800801if (!ktxFormatSize_initFromDfd(&This->_protected->_formatSize, This->pDfd)) {802result = KTX_UNSUPPORTED_TEXTURE_TYPE;803goto cleanup;804}805This->isCompressed = (This->_protected->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);806807if (This->supercompressionScheme == KTX_SS_BASIS_LZ && pBDFD->model != KHR_DF_MODEL_ETC1S) {808result = KTX_FILE_DATA_ERROR;809goto cleanup;810}811812// Check compatibility with the KHR_texture_basisu glTF extension, if needed.813if (createFlags & KTX_TEXTURE_CREATE_CHECK_GLTF_BASISU_BIT) {814uint32_t max_dim = MAX(MAX(pHeader->pixelWidth, pHeader->pixelHeight), pHeader->pixelDepth);815uint32_t full_mip_pyramid_level_count = 1 + (uint32_t)log2(max_dim);816if (pHeader->levelCount != 1 && pHeader->levelCount != full_mip_pyramid_level_count) {817// KHR_texture_basisu requires full mip pyramid or single mip level818result = KTX_FILE_DATA_ERROR;819goto cleanup;820}821if (This->numDimensions != 2 || This->isArray || This->isCubemap) {822// KHR_texture_basisu requires 2D textures.823result = KTX_FILE_DATA_ERROR;824goto cleanup;825}826if ((This->baseWidth % 4) != 0 || (This->baseHeight % 4) != 0) {827// KHR_texture_basisu requires width and height to be a multiple of 4.828result = KTX_FILE_DATA_ERROR;829goto cleanup;830}831if (pBDFD->model != KHR_DF_MODEL_ETC1S && pBDFD->model != KHR_DF_MODEL_UASTC) {832// KHR_texture_basisu requires BasisLZ or UASTC833result = KTX_FILE_DATA_ERROR;834goto cleanup;835}836if (pBDFD->model == KHR_DF_MODEL_UASTC &&837This->supercompressionScheme != KTX_SS_NONE &&838This->supercompressionScheme != KTX_SS_ZSTD) {839// KHR_texture_basisu only allows NONE and ZSTD supercompression for UASTC840result = KTX_FILE_DATA_ERROR;841goto cleanup;842}843}844845uint32_t sampleCount = KHR_DFDSAMPLECOUNT(This->pDfd + 1);846if (sampleCount == 0) {847// Invalid sample count848result = KTX_FILE_DATA_ERROR;849goto cleanup;850}851if (pBDFD->model == KHR_DF_MODEL_ETC1S || pBDFD->model == KHR_DF_MODEL_UASTC) {852if (sampleCount < 1 || sampleCount > 2 || (sampleCount == 2 && pBDFD->model == KHR_DF_MODEL_UASTC)) {853// Invalid sample count854result = KTX_FILE_DATA_ERROR;855goto cleanup;856}857if (pBDFD->texelBlockDimension0 != 3 || pBDFD->texelBlockDimension1 != 3 ||858pBDFD->texelBlockDimension2 != 0 || pBDFD->texelBlockDimension3 != 0) {859// Texel block dimension must be 4x4x1x1 (offset by one)860result = KTX_FILE_DATA_ERROR;861goto cleanup;862}863}864865This->_private->_requiredLevelAlignment866= ktxTexture2_calcRequiredLevelAlignment(This);867868// Make an empty hash list.869ktxHashList_Construct(&This->kvDataHead);870// Load KVData.871if (pHeader->keyValueData.byteLength > 0) {872uint32_t expectedOffset = pHeader->dataFormatDescriptor.byteOffset + pHeader->dataFormatDescriptor.byteLength;873expectedOffset = (expectedOffset + 3) & ~0x3; // 4 byte aligned874if (pHeader->keyValueData.byteOffset != expectedOffset) {875result = KTX_FILE_DATA_ERROR;876goto cleanup;877}878if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) {879ktx_uint32_t kvdLen = pHeader->keyValueData.byteLength;880ktx_uint8_t* pKvd;881882pKvd = malloc(kvdLen);883if (pKvd == NULL) {884result = KTX_OUT_OF_MEMORY;885goto cleanup;886}887888result = stream->read(stream, pKvd, kvdLen);889if (result != KTX_SUCCESS)890goto cleanup;891892if (IS_BIG_ENDIAN) {893/* Swap the counts inside the key & value data. */894ktx_uint8_t* src = pKvd;895ktx_uint8_t* end = pKvd + kvdLen;896while (src < end) {897ktx_uint32_t keyAndValueByteSize = *((ktx_uint32_t*)src);898_ktxSwapEndian32(&keyAndValueByteSize, 1);899src += _KTX_PAD4(keyAndValueByteSize);900}901}902903if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) {904char* orientationStr;905ktx_uint32_t orientationLen;906ktx_uint32_t animData[3];907ktx_uint32_t animDataLen;908909result = ktxHashList_Deserialize(&This->kvDataHead,910kvdLen, pKvd);911free(pKvd);912if (result != KTX_SUCCESS) {913goto cleanup;914}915916result = ktxHashList_FindValue(&This->kvDataHead,917KTX_ORIENTATION_KEY,918&orientationLen,919(void**)&orientationStr);920assert(result != KTX_INVALID_VALUE);921if (result == KTX_SUCCESS) {922// Length includes the terminating NUL.923if (orientationLen != This->numDimensions + 1) {924// There needs to be an entry for each dimension of925// the texture.926result = KTX_FILE_DATA_ERROR;927goto cleanup;928} else {929switch (This->numDimensions) {930case 3:931This->orientation.z = orientationStr[2];932FALLTHROUGH;933case 2:934This->orientation.y = orientationStr[1];935FALLTHROUGH;936case 1:937This->orientation.x = orientationStr[0];938}939}940} else {941result = KTX_SUCCESS; // Not finding orientation is okay.942}943result = ktxHashList_FindValue(&This->kvDataHead,944KTX_ANIMDATA_KEY,945&animDataLen,946(void**)animData);947assert(result != KTX_INVALID_VALUE);948if (result == KTX_SUCCESS) {949if (animDataLen != sizeof(animData)) {950result = KTX_FILE_DATA_ERROR;951goto cleanup;952}953if (This->isArray) {954This->isVideo = KTX_TRUE;955This->duration = animData[0];956This->timescale = animData[1];957This->loopcount = animData[2];958} else {959// animData is only valid for array textures.960result = KTX_FILE_DATA_ERROR;961goto cleanup;962}963} else {964result = KTX_SUCCESS; // Not finding video is okay.965}966} else {967This->kvDataLen = kvdLen;968This->kvData = pKvd;969}970} else {971stream->skip(stream, pHeader->keyValueData.byteLength);972}973} else if (pHeader->keyValueData.byteOffset != 0) {974// Non-zero KVD byteOffset with zero byteLength975result = KTX_FILE_DATA_ERROR;976goto cleanup;977}978979if (pHeader->supercompressionGlobalData.byteLength > 0) {980switch (This->supercompressionScheme) {981case KTX_SS_BASIS_LZ:982break;983case KTX_SS_NONE:984case KTX_SS_ZSTD:985case KTX_SS_ZLIB:986// In these cases SGD is not allowed987result = KTX_FILE_DATA_ERROR;988break;989default:990// We don't support other supercompression schemes991result = KTX_UNSUPPORTED_FEATURE;992break;993}994if (result != KTX_SUCCESS)995goto cleanup;996997// There could be padding here so seek to the next item.998result = stream->setpos(stream,999pHeader->supercompressionGlobalData.byteOffset);1000if (result != KTX_SUCCESS)1001goto cleanup;10021003// Read supercompressionGlobalData1004private->_supercompressionGlobalData =1005(ktx_uint8_t*)malloc(pHeader->supercompressionGlobalData.byteLength);1006if (!private->_supercompressionGlobalData) {1007result = KTX_OUT_OF_MEMORY;1008goto cleanup;1009}1010private->_sgdByteLength1011= pHeader->supercompressionGlobalData.byteLength;1012result = stream->read(stream, private->_supercompressionGlobalData,1013private->_sgdByteLength);10141015if (result != KTX_SUCCESS)1016goto cleanup;1017} else if (pHeader->supercompressionGlobalData.byteOffset != 0) {1018// Non-zero SGD byteOffset with zero byteLength1019result = KTX_FILE_DATA_ERROR;1020goto cleanup;1021} else if (This->supercompressionScheme == KTX_SS_BASIS_LZ) {1022// SGD is required for BasisLZ1023result = KTX_FILE_DATA_ERROR;1024goto cleanup;1025}10261027// Calculate size of the image data. Level 0 is the last level in the data.1028This->dataSize = private->_levelIndex[0].byteOffset1029+ private->_levelIndex[0].byteLength;10301031/*1032* Load the images, if requested.1033*/1034if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) {1035result = ktxTexture2_LoadImageData(This, NULL, 0);1036}1037if (result != KTX_SUCCESS)1038goto cleanup;10391040return result;10411042cleanup:1043ktxTexture2_destruct(This);1044return result;1045}10461047/**1048* @memberof ktxTexture2 @private1049* @~English1050* @brief Construct a ktxTexture from a ktxStream reading from a KTX source.1051*1052* The stream object is copied into the constructed ktxTexture2.1053*1054* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1055* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1056* will minimize memory usage by allowing, for example, loading the images1057* directly from the source into a Vulkan staging buffer.1058*1059* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1060* provided solely to enable implementation of the @e libktx v1 API on top of1061* ktxTexture.1062*1063* @param[in] This pointer to a ktxTexture2-sized block of memory to1064* initialize.1065* @param[in] pStream pointer to the stream to read.1066* @param[in] createFlags bitmask requesting specific actions during creation.1067*1068* @return KTX_SUCCESS on success, other KTX_* enum values on error.1069*1070* @exception KTX_FILE_READ_ERROR1071* An error occurred while reading the source.1072*1073* For other exceptions see ktxTexture2_constructFromStreamAndHeader().1074*/1075static KTX_error_code1076ktxTexture2_constructFromStream(ktxTexture2* This, ktxStream* pStream,1077ktxTextureCreateFlags createFlags)1078{1079KTX_header2 header;1080KTX_error_code result;10811082// Read header.1083result = pStream->read(pStream, &header, KTX2_HEADER_SIZE);1084if (result != KTX_SUCCESS)1085return result;10861087#if IS_BIG_ENDIAN1088// byte swap the header1089#endif1090return ktxTexture2_constructFromStreamAndHeader(This, pStream,1091&header, createFlags);1092}10931094/**1095* @memberof ktxTexture2 @private1096* @~English1097* @brief Construct a ktxTexture from a stdio stream reading from a KTX source.1098*1099* See ktxTextureInt_constructFromStream for details.1100*1101* @note Do not close the stdio stream until you are finished with the texture1102* object.1103*1104* @param[in] This pointer to a ktxTextureInt-sized block of memory to1105* initialize.1106* @param[in] stdioStream a stdio FILE pointer opened on the source.1107* @param[in] createFlags bitmask requesting specific actions during creation.1108*1109* @return KTX_SUCCESS on success, other KTX_* enum values on error.1110*1111* @exception KTX_INVALID_VALUE Either @p stdiostream or @p This is null.1112*1113* For other exceptions, see ktxTexture_constructFromStream().1114*/1115static KTX_error_code1116ktxTexture2_constructFromStdioStream(ktxTexture2* This, FILE* stdioStream,1117ktxTextureCreateFlags createFlags)1118{1119KTX_error_code result;1120ktxStream stream;11211122if (stdioStream == NULL || This == NULL)1123return KTX_INVALID_VALUE;11241125result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);1126if (result == KTX_SUCCESS)1127result = ktxTexture2_constructFromStream(This, &stream, createFlags);1128return result;1129}11301131/**1132* @memberof ktxTexture2 @private1133* @~English1134* @brief Construct a ktxTexture from a named KTX file.1135*1136* The file name must be encoded in utf-8. On Windows convert unicode names1137* to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.1138*1139* See ktxTextureInt_constructFromStream for details.1140*1141* @param[in] This pointer to a ktxTextureInt-sized block of memory to1142* initialize.1143* @param[in] filename pointer to a char array containing the file name.1144* @param[in] createFlags bitmask requesting specific actions during creation.1145*1146* @return KTX_SUCCESS on success, other KTX_* enum values on error.1147*1148* @exception KTX_FILE_OPEN_FAILED The file could not be opened.1149* @exception KTX_INVALID_VALUE @p filename is @c NULL.1150*1151* For other exceptions, see ktxTexture_constructFromStream().1152*/1153static KTX_error_code1154ktxTexture2_constructFromNamedFile(ktxTexture2* This,1155const char* const filename,1156ktxTextureCreateFlags createFlags)1157{1158KTX_error_code result;1159ktxStream stream;1160FILE* file;11611162if (This == NULL || filename == NULL)1163return KTX_INVALID_VALUE;11641165file = ktxFOpenUTF8(filename, "rb");1166if (!file)1167return KTX_FILE_OPEN_FAILED;11681169result = ktxFileStream_construct(&stream, file, KTX_TRUE);1170if (result == KTX_SUCCESS)1171result = ktxTexture2_constructFromStream(This, &stream, createFlags);11721173return result;1174}11751176/**1177* @memberof ktxTexture2 @private1178* @~English1179* @brief Construct a ktxTexture from KTX-formatted data in memory.1180*1181* See ktxTextureInt_constructFromStream for details.1182*1183* @param[in] This pointer to a ktxTextureInt-sized block of memory to1184* initialize.1185* @param[in] bytes pointer to the memory containing the serialized KTX data.1186* @param[in] size length of the KTX data in bytes.1187* @param[in] createFlags bitmask requesting specific actions during creation.1188*1189* @return KTX_SUCCESS on success, other KTX_* enum values on error.1190*1191* @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.1192*1193* For other exceptions, see ktxTexture_constructFromStream().1194*/1195static KTX_error_code1196ktxTexture2_constructFromMemory(ktxTexture2* This,1197const ktx_uint8_t* bytes, ktx_size_t size,1198ktxTextureCreateFlags createFlags)1199{1200KTX_error_code result;1201ktxStream stream;12021203if (bytes == NULL || size == 0)1204return KTX_INVALID_VALUE;12051206result = ktxMemStream_construct_ro(&stream, bytes, size);1207if (result == KTX_SUCCESS)1208result = ktxTexture2_constructFromStream(This, &stream, createFlags);12091210return result;1211}12121213/**1214* @memberof ktxTexture2 @private1215* @~English1216* @brief Destruct a ktxTexture2, freeing and internal memory.1217*1218* @param[in] This pointer to a ktxTexture2-sized block of memory to1219* initialize.1220*/1221void1222ktxTexture2_destruct(ktxTexture2* This)1223{1224if (This->pDfd) free(This->pDfd);1225if (This->_private) {1226ktx_uint8_t* sgd = This->_private->_supercompressionGlobalData;1227if (sgd) free(sgd);1228free(This->_private);1229}1230ktxTexture_destruct(ktxTexture(This));1231}12321233/*1234* In hindsight this function should have been `#if KTX_FEATURE_WRITE`.1235* In the interest of not breaking an app that may be using this in1236* `libktx_read` we won't change it.1237*/1238/**1239* @memberof ktxTexture21240* @ingroup writer1241* @~English1242* @brief Create a new empty ktxTexture2.1243*1244* The address of the newly created ktxTexture2 is written to the location1245* pointed at by @p newTex.1246*1247* @param[in] createInfo pointer to a ktxTextureCreateInfo struct with1248* information describing the texture.1249* @param[in] storageAllocation1250* enum indicating whether or not to allocate storage1251* for the texture images.1252* @param[in,out] newTex pointer to a location in which store the address of1253* the newly created texture.1254*1255* @return KTX_SUCCESS on success, other KTX_* enum values on error.1256*1257* @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a1258* valid OpenGL internal format value.1259* @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 21260* or 3.1261* @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in1262* @p createInfo is 0.1263* @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.1264* @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.1265* @exception KTX_INVALID_OPERATION1266* The <tt>base{Width,Height,Depth}</tt> specified1267* in @p createInfo are inconsistent with1268* @c numDimensions.1269* @exception KTX_INVALID_OPERATION1270* @p createInfo is requesting a 3D array or1271* 3D cubemap texture.1272* @exception KTX_INVALID_OPERATION1273* @p createInfo is requesting a cubemap with1274* non-square or non-2D images.1275* @exception KTX_INVALID_OPERATION1276* @p createInfo is requesting more mip levels1277* than needed for the specified1278* <tt>base{Width,Height,Depth}</tt>.1279* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture's images.1280*/1281KTX_error_code1282ktxTexture2_Create(const ktxTextureCreateInfo* const createInfo,1283ktxTextureCreateStorageEnum storageAllocation,1284ktxTexture2** newTex)1285{1286KTX_error_code result;12871288if (newTex == NULL)1289return KTX_INVALID_VALUE;12901291ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1292if (tex == NULL)1293return KTX_OUT_OF_MEMORY;12941295result = ktxTexture2_construct(tex, createInfo, storageAllocation);1296if (result != KTX_SUCCESS) {1297free(tex);1298} else {1299*newTex = tex;1300}1301return result;1302}13031304/**1305* @memberof ktxTexture21306* @ingroup writer1307* @~English1308* @brief Create a ktxTexture2 by making a copy of a ktxTexture2.1309*1310* The address of the newly created ktxTexture2 is written to the location1311* pointed at by @p newTex.1312*1313* @param[in] orig pointer to the texture to copy.1314* @param[in,out] newTex pointer to a location in which store the address of1315* the newly created texture.1316*1317* @return KTX_SUCCESS on success, other KTX_* enum values on error.1318*1319* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.1320*/1321KTX_error_code1322ktxTexture2_CreateCopy(ktxTexture2* orig, ktxTexture2** newTex)1323{1324KTX_error_code result;13251326if (newTex == NULL)1327return KTX_INVALID_VALUE;13281329ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1330if (tex == NULL)1331return KTX_OUT_OF_MEMORY;13321333result = ktxTexture2_constructCopy(tex, orig);1334if (result != KTX_SUCCESS) {1335free(tex);1336} else {1337*newTex = tex;1338}1339return result;13401341}13421343/**1344* @defgroup reader Reader1345* @brief Read KTX-formatted data.1346* @{1347*/13481349/**1350* @memberof ktxTexture21351* @~English1352* @brief Create a ktxTexture2 from a stdio stream reading from a KTX source.1353*1354* The address of a newly created ktxTexture2 reflecting the contents of the1355* stdio stream is written to the location pointed at by @p newTex.1356*1357* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1358* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1359* will minimize memory usage by allowing, for example, loading the images1360* directly from the source into a Vulkan staging buffer.1361*1362* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1363* provided solely to enable implementation of the @e libktx v1 API on top of1364* ktxTexture.1365*1366* @param[in] stdioStream stdio FILE pointer created from the desired file.1367* @param[in] createFlags bitmask requesting specific actions during creation.1368* @param[in,out] newTex pointer to a location in which store the address of1369* the newly created texture.1370*1371* @return KTX_SUCCESS on success, other KTX_* enum values on error.1372*1373* @exception KTX_INVALID_VALUE @p newTex is @c NULL.1374* @exception KTX_FILE_DATA_ERROR1375* Source data is inconsistent with the KTX1376* specification.1377* @exception KTX_FILE_READ_ERROR1378* An error occurred while reading the source.1379* @exception KTX_FILE_UNEXPECTED_EOF1380* Not enough data in the source.1381* @exception KTX_OUT_OF_MEMORY Not enough memory to create the texture object,1382* load the images or load the key-value data.1383* @exception KTX_UNKNOWN_FILE_FORMAT1384* The source is not in KTX format.1385* @exception KTX_UNSUPPORTED_TEXTURE_TYPE1386* The source describes a texture type not1387* supported by OpenGL or Vulkan, e.g, a 3D array.1388*/1389KTX_error_code1390ktxTexture2_CreateFromStdioStream(FILE* stdioStream,1391ktxTextureCreateFlags createFlags,1392ktxTexture2** newTex)1393{1394KTX_error_code result;1395if (newTex == NULL)1396return KTX_INVALID_VALUE;13971398ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1399if (tex == NULL)1400return KTX_OUT_OF_MEMORY;14011402result = ktxTexture2_constructFromStdioStream(tex, stdioStream,1403createFlags);1404if (result == KTX_SUCCESS)1405*newTex = (ktxTexture2*)tex;1406else {1407free(tex);1408*newTex = NULL;1409}1410return result;1411}14121413/**1414* @memberof ktxTexture21415* @~English1416* @brief Create a ktxTexture2 from a named KTX file.1417*1418* The address of a newly created ktxTexture2 reflecting the contents of the1419* file is written to the location pointed at by @p newTex.1420*1421* The file name must be encoded in utf-8. On Windows convert unicode names1422* to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.1423*1424* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1425* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1426* will minimize memory usage by allowing, for example, loading the images1427* directly from the source into a Vulkan staging buffer.1428*1429* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1430* provided solely to enable implementation of the @e libktx v1 API on top of1431* ktxTexture.1432*1433* @param[in] filename pointer to a char array containing the file name.1434* @param[in] createFlags bitmask requesting specific actions during creation.1435* @param[in,out] newTex pointer to a location in which store the address of1436* the newly created texture.1437*1438* @return KTX_SUCCESS on success, other KTX_* enum values on error.14391440* @exception KTX_FILE_OPEN_FAILED The file could not be opened.1441* @exception KTX_INVALID_VALUE @p filename is @c NULL.1442*1443* For other exceptions, see ktxTexture2_CreateFromStdioStream().1444*/1445KTX_error_code1446ktxTexture2_CreateFromNamedFile(const char* const filename,1447ktxTextureCreateFlags createFlags,1448ktxTexture2** newTex)1449{1450KTX_error_code result;14511452if (newTex == NULL)1453return KTX_INVALID_VALUE;14541455ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1456if (tex == NULL)1457return KTX_OUT_OF_MEMORY;14581459result = ktxTexture2_constructFromNamedFile(tex, filename, createFlags);1460if (result == KTX_SUCCESS)1461*newTex = (ktxTexture2*)tex;1462else {1463free(tex);1464*newTex = NULL;1465}1466return result;1467}14681469/**1470* @memberof ktxTexture21471* @~English1472* @brief Create a ktxTexture2 from KTX-formatted data in memory.1473*1474* The address of a newly created ktxTexture2 reflecting the contents of the1475* serialized KTX data is written to the location pointed at by @p newTex.1476*1477* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1478* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1479* will minimize memory usage by allowing, for example, loading the images1480* directly from the source into a Vulkan staging buffer.1481*1482* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1483* provided solely to enable implementation of the @e libktx v1 API on top of1484* ktxTexture.1485*1486* @param[in] bytes pointer to the memory containing the serialized KTX data.1487* @param[in] size length of the KTX data in bytes.1488* @param[in] createFlags bitmask requesting specific actions during creation.1489* @param[in,out] newTex pointer to a location in which store the address of1490* the newly created texture.1491*1492* @return KTX_SUCCESS on success, other KTX_* enum values on error.1493*1494* @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.1495*1496* For other exceptions, see ktxTexture2_CreateFromStdioStream().1497*/1498KTX_error_code1499ktxTexture2_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,1500ktxTextureCreateFlags createFlags,1501ktxTexture2** newTex)1502{1503KTX_error_code result;1504if (newTex == NULL)1505return KTX_INVALID_VALUE;15061507ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1508if (tex == NULL)1509return KTX_OUT_OF_MEMORY;15101511result = ktxTexture2_constructFromMemory(tex, bytes, size,1512createFlags);1513if (result == KTX_SUCCESS)1514*newTex = (ktxTexture2*)tex;1515else {1516free(tex);1517*newTex = NULL;1518}1519return result;1520}15211522/**1523* @memberof ktxTexture21524* @~English1525* @brief Create a ktxTexture2 from KTX-formatted data from a stream.1526*1527* The address of a newly created ktxTexture2 reflecting the contents of the1528* serialized KTX data is written to the location pointed at by @p newTex.1529*1530* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1531* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1532* will minimize memory usage by allowing, for example, loading the images1533* directly from the source into a Vulkan staging buffer.1534*1535* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1536* provided solely to enable implementation of the @e libktx v1 API on top of1537* ktxTexture.1538*1539* @param[in] stream pointer to the stream to read KTX data from.1540* @param[in] createFlags bitmask requesting specific actions during creation.1541* @param[in,out] newTex pointer to a location in which store the address of1542* the newly created texture.1543*1544* @return KTX_SUCCESS on success, other KTX_* enum values on error.1545*1546* @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.1547*1548* For other exceptions, see ktxTexture2_CreateFromStdioStream().1549*/1550KTX_error_code1551ktxTexture2_CreateFromStream(ktxStream* stream,1552ktxTextureCreateFlags createFlags,1553ktxTexture2** newTex)1554{1555KTX_error_code result;1556if (newTex == NULL)1557return KTX_INVALID_VALUE;15581559ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1560if (tex == NULL)1561return KTX_OUT_OF_MEMORY;15621563result = ktxTexture2_constructFromStream(tex, stream, createFlags);1564if (result == KTX_SUCCESS)1565*newTex = (ktxTexture2*)tex;1566else {1567free(tex);1568*newTex = NULL;1569}1570return result;1571}15721573/**1574* @memberof ktxTexture21575* @~English1576* @brief Destroy a ktxTexture2 object.1577*1578* This frees the memory associated with the texture contents and the memory1579* of the ktxTexture2 object. This does @e not delete any OpenGL or Vulkan1580* texture objects created by ktxTexture2_GLUpload or ktxTexture2_VkUpload.1581*1582* @param[in] This pointer to the ktxTexture2 object to destroy1583*/1584void1585ktxTexture2_Destroy(ktxTexture2* This)1586{1587ktxTexture2_destruct(This);1588free(This);1589}15901591/**1592* @memberof ktxTexture2 @private1593* @~English1594* @brief Calculate the size of the image data for the specified number1595* of levels.1596*1597* The data size is the sum of the sizes of each level up to the number1598* specified and includes any @c mipPadding between levels. It does1599* not include initial @c mipPadding required in the file.1600*1601* @param[in] This pointer to the ktxTexture object of interest.1602* @param[in] levels number of levels whose data size to return.1603*1604* @return the data size in bytes.1605*/1606ktx_size_t1607ktxTexture2_calcDataSizeLevels(ktxTexture2* This, ktx_uint32_t levels)1608{1609ktx_size_t dataSize = 0;16101611assert(This != NULL);1612assert(This->supercompressionScheme == KTX_SS_NONE);1613assert(levels <= This->numLevels);1614for (ktx_uint32_t i = levels - 1; i > 0; i--) {1615ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,1616KTX_FORMAT_VERSION_TWO);1617dataSize += _KTX_PADN(This->_private->_requiredLevelAlignment,1618levelSize);1619}1620dataSize += ktxTexture_calcLevelSize(ktxTexture(This), 0,1621KTX_FORMAT_VERSION_TWO);1622return dataSize;1623}16241625/**1626* @memberof ktxTexture2 @private1627* @~English1628*1629* @copydoc ktxTexture::ktxTexture_doCalcFaceLodSize1630*/1631ktx_size_t1632ktxTexture2_calcFaceLodSize(ktxTexture2* This, ktx_uint32_t level)1633{1634assert(This != NULL);1635assert(This->supercompressionScheme == KTX_SS_NONE);1636/*1637* For non-array cubemaps this is the size of a face. For everything1638* else it is the size of the level.1639*/1640if (This->isCubemap && !This->isArray)1641return ktxTexture_calcImageSize(ktxTexture(This), level,1642KTX_FORMAT_VERSION_TWO);1643else1644return This->_private->_levelIndex[level].uncompressedByteLength;1645}16461647/**1648* @memberof ktxTexture2 @private1649* @~English1650* @brief Return the offset of a level in bytes from the start of the image1651* data in a ktxTexture.1652*1653* Since the offset is from the start of the image data, it does not include the initial1654* @c mipPadding required in the file.1655*1656* @param[in] This pointer to the ktxTexture object of interest.1657* @param[in] level level whose offset to return.1658*1659* @return the data size in bytes.1660*/1661ktx_size_t1662ktxTexture2_calcLevelOffset(ktxTexture2* This, ktx_uint32_t level)1663{1664assert (This != NULL);1665assert(This->supercompressionScheme == KTX_SS_NONE);1666assert (level < This->numLevels);1667ktx_size_t levelOffset = 0;1668for (ktx_uint32_t i = This->numLevels - 1; i > level; i--) {1669ktx_size_t levelSize;1670levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,1671KTX_FORMAT_VERSION_TWO);1672levelOffset += _KTX_PADN(This->_private->_requiredLevelAlignment,1673levelSize);1674}1675return levelOffset;1676}167716781679/**1680* @memberof ktxTexture2 @private1681* @~English1682* @brief Retrieve the offset of a level's first image within the KTX2 file.1683*1684* @param[in] This pointer to the ktxTexture object of interest.1685*/1686ktx_uint64_t ktxTexture2_levelFileOffset(ktxTexture2* This, ktx_uint32_t level)1687{1688assert(This->_private->_firstLevelFileOffset != 0);1689return This->_private->_levelIndex[level].byteOffset1690+ This->_private->_firstLevelFileOffset;1691}16921693// Recursive function to return the greatest common divisor of a and b.1694static uint32_t1695gcd(uint32_t a, uint32_t b) {1696if (a == 0)1697return b;1698return gcd(b % a, a);1699}17001701// Function to return the least common multiple of a & 4.1702uint32_t1703lcm4(uint32_t a)1704{1705if (!(a & 0x03))1706return a; // a is a multiple of 4.1707return (a*4) / gcd(a, 4);1708}17091710/**1711* @memberof ktxTexture2 @private1712* @~English1713* @brief Return the required alignment for levels of this texture.1714*1715* @param[in] This pointer to the ktxTexture2 object of interest.1716*1717* @return The required alignment for levels.1718*/1719ktx_uint32_t1720ktxTexture2_calcRequiredLevelAlignment(ktxTexture2* This)1721{1722ktx_uint32_t alignment;1723if (This->supercompressionScheme != KTX_SS_NONE)1724alignment = 1;1725else1726alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);1727return alignment;1728}17291730/**1731* @memberof ktxTexture2 @private1732* @~English1733* @brief Return what the required alignment for levels of this texture will be after inflation.1734*1735* @param[in] This pointer to the ktxTexture2 object of interest.1736*1737* @return The required alignment for levels.1738*/1739ktx_uint32_t1740ktxTexture2_calcPostInflationLevelAlignment(ktxTexture2* This)1741{1742ktx_uint32_t alignment;17431744// Should actually work for none supercompressed but don't want to1745// encourage use of it.1746assert(This->supercompressionScheme != KTX_SS_NONE && This->supercompressionScheme != KTX_SS_BASIS_LZ);17471748if (This->vkFormat != VK_FORMAT_UNDEFINED)1749alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);1750else1751alignment = 16;17521753return alignment;1754}175517561757/**1758* @memberof ktxTexture21759* @~English1760* @brief Return information about the components of an image in a texture.1761*1762* @param[in] This pointer to the ktxTexture object of interest.1763* @param[in,out] pNumComponents pointer to location in which to write the1764* number of components in the textures images.1765* @param[in,out] pComponentByteLength1766* pointer to the location in which to write1767* byte length of a component.1768*/1769void1770ktxTexture2_GetComponentInfo(ktxTexture2* This, uint32_t* pNumComponents,1771uint32_t* pComponentByteLength)1772{1773// FIXME Need to handle packed case.1774getDFDComponentInfoUnpacked(This->pDfd, pNumComponents,1775pComponentByteLength);1776}17771778/**1779* @memberof ktxTexture21780* @~English1781* @brief Return the number of components in an image of the texture.1782*1783* Returns the number of components indicated by the DFD's sample information1784* in accordance with the color model. For uncompressed formats it will be the actual1785* number of components in the image. For block-compressed formats, it will be 1 or 21786* according to the format's DFD color model. For Basis compressed textures, the1787* function examines the ids of the channels indicated by the DFD and uses that1788* information to determine and return the number of components in the image1789* @e before encoding and deflation so it can be used to help choose a suitable1790* transcode target format.1791*1792* @param[in] This pointer to the ktxTexture object of interest.1793*1794* @return the number of components.1795*/1796ktx_uint32_t1797ktxTexture2_GetNumComponents(ktxTexture2* This)1798{1799uint32_t* pBdb = This->pDfd + 1;1800uint32_t dfdNumComponents = getDFDNumComponents(This->pDfd);1801uint32_t colorModel = KHR_DFDVAL(pBdb, MODEL);1802if (colorModel < KHR_DF_MODEL_DXT1A) {1803return dfdNumComponents;1804} else {1805switch (colorModel) {1806case KHR_DF_MODEL_ETC1S:1807{1808uint32_t channel0Id = KHR_DFDSVAL(pBdb, 0, CHANNELID);1809if (dfdNumComponents == 1) {1810if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB)1811return 3;1812else1813return 1;1814} else {1815uint32_t channel1Id = KHR_DFDSVAL(pBdb, 1, CHANNELID);1816if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB1817&& channel1Id == KHR_DF_CHANNEL_ETC1S_AAA)1818return 4;1819else {1820// An invalid combination of channel Ids should never1821// have been set during creation or should have been1822// caught when the file was loaded.1823assert(channel0Id == KHR_DF_CHANNEL_ETC1S_RRR1824&& channel1Id == KHR_DF_CHANNEL_ETC1S_GGG);1825return 2;1826}1827}1828break;1829}1830case KHR_DF_MODEL_UASTC:1831switch (KHR_DFDSVAL(pBdb, 0, CHANNELID)) {1832case KHR_DF_CHANNEL_UASTC_RRR:1833return 1;1834case KHR_DF_CHANNEL_UASTC_RRRG:1835return 2;1836case KHR_DF_CHANNEL_UASTC_RGB:1837return 3;1838case KHR_DF_CHANNEL_UASTC_RGBA:1839return 4;1840default:1841// Same comment as for the assert in the ETC1 case.1842assert(false);1843return 1;1844}1845break;1846default:1847return dfdNumComponents;1848}1849}1850}18511852/**1853* @memberof ktxTexture21854* @~English1855* @brief Find the offset of an image within a ktxTexture's image data.1856*1857* As there is no such thing as a 3D cubemap we make the 3rd location parameter1858* do double duty. Only works for non-supercompressed textures as1859* there is no way to tell where an image is for a supercompressed one.1860*1861* @param[in] This pointer to the ktxTexture object of interest.1862* @param[in] level mip level of the image.1863* @param[in] layer array layer of the image.1864* @param[in] faceSlice cube map face or depth slice of the image.1865* @param[in,out] pOffset pointer to location to store the offset.1866*1867* @return KTX_SUCCESS on success, other KTX_* enum values on error.1868*1869* @exception KTX_INVALID_OPERATION1870* @p level, @p layer or @p faceSlice exceed the1871* dimensions of the texture.1872* @exception KTX_INVALID_OPERATION Texture is supercompressed.1873* @exception KTX_INVALID_VALID @p This is NULL.1874*/1875KTX_error_code1876ktxTexture2_GetImageOffset(ktxTexture2* This, ktx_uint32_t level,1877ktx_uint32_t layer, ktx_uint32_t faceSlice,1878ktx_size_t* pOffset)1879{1880if (This == NULL)1881return KTX_INVALID_VALUE;18821883if (level >= This->numLevels || layer >= This->numLayers)1884return KTX_INVALID_OPERATION;18851886if (This->supercompressionScheme != KTX_SS_NONE)1887return KTX_INVALID_OPERATION;18881889if (This->isCubemap) {1890if (faceSlice >= This->numFaces)1891return KTX_INVALID_OPERATION;1892} else {1893ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level);1894if (faceSlice >= maxSlice)1895return KTX_INVALID_OPERATION;1896}18971898// Get the offset of the start of the level.1899*pOffset = ktxTexture2_levelDataOffset(This, level);19001901// All layers, faces & slices within a level are the same size.1902if (layer != 0) {1903ktx_size_t layerSize;1904layerSize = ktxTexture_layerSize(ktxTexture(This), level,1905KTX_FORMAT_VERSION_TWO);1906*pOffset += layer * layerSize;1907}1908if (faceSlice != 0) {1909ktx_size_t imageSize;1910imageSize = ktxTexture2_GetImageSize(This, level);1911*pOffset += faceSlice * imageSize;1912}1913return KTX_SUCCESS;1914}19151916/**1917* @memberof ktxTexture21918* @~English1919* @brief Retrieve the transfer function of the images.1920*1921* @param[in] This pointer to the ktxTexture2 object of interest.1922*1923* @return A @c khr_df_transfer enum value specifying the transfer function.1924*/1925khr_df_transfer_e1926ktxTexture2_GetTransferFunction_e(ktxTexture2* This)1927{1928return KHR_DFDVAL(This->pDfd+1, TRANSFER);1929}19301931/**1932* @memberof ktxTexture21933* @~English1934* @brief Retrieve the transfer function of the images.1935* @deprecated Use ktxTexture2\_GetTransferFunction\_e. Now that the KTX1936* specification allows setting of non-linear transfer functions other than1937* sRGB, it is possible for the transfer function to be an EOTF so this1938* name is no longer appropriate.1939*1940* @param[in] This pointer to the ktxTexture2 object of interest.1941*1942* @return A @c khr_df_transfer enum value specifying the transfer function.1943*/1944khr_df_transfer_e1945ktxTexture2_GetOETF_e(ktxTexture2* This)1946{1947return KHR_DFDVAL(This->pDfd+1, TRANSFER);1948}19491950/**1951* @memberof ktxTexture21952* @~English1953* @brief Retrieve the transfer function of the images.1954* @deprecated Use ktxTexture2\_GetTransferFunction\_e.1955*1956* @param[in] This pointer to the ktxTexture2 object of interest.1957*1958* @return A @c khr_df_transfer enum value specifying the transfer function,1959* returned as @c ktx_uint32_t.1960*/1961ktx_uint32_t1962ktxTexture2_GetOETF(ktxTexture2* This)1963{1964return KHR_DFDVAL(This->pDfd+1, TRANSFER);1965}19661967/**1968* @memberof ktxTexture21969* @~English1970* @brief Retrieve the DFD color model of the images.1971*1972* @param[in] This pointer to the ktxTexture2 object of interest.1973*1974* @return A @c khr_df_transfer enum value specifying the color model.1975*/1976khr_df_model_e1977ktxTexture2_GetColorModel_e(ktxTexture2* This)1978{1979return KHR_DFDVAL(This->pDfd+1, MODEL);1980}19811982/**1983* @memberof ktxTexture21984* @~English1985* @brief Retrieve whether the RGB components have been premultiplied by the alpha component.1986*1987* @param[in] This pointer to the ktxTexture2 object of interest.1988*1989* @return KTX\_TRUE if the components are premultiplied, KTX_FALSE otherwise.1990*/1991ktx_bool_t1992ktxTexture2_GetPremultipliedAlpha(ktxTexture2* This)1993{1994return KHR_DFDVAL(This->pDfd+1, FLAGS) & KHR_DF_FLAG_ALPHA_PREMULTIPLIED;1995}19961997/**1998* @memberof ktxTexture21999* @~English2000* @brief Retrieve the color primaries of the images.2001*2002* @param[in] This pointer to the ktxTexture2 object of interest.2003*2004* @return A @c khr_df_primaries enum value specifying the primaries.2005*/2006khr_df_primaries_e2007ktxTexture2_GetPrimaries_e(ktxTexture2* This)2008{2009return KHR_DFDVAL(This->pDfd+1, PRIMARIES);2010}20112012/**2013* @memberof ktxTexture22014* @~English2015* @brief Query if the images are in a transcodable format.2016*2017* @param[in] This pointer to the ktxTexture2 object of interest.2018*/2019ktx_bool_t2020ktxTexture2_NeedsTranscoding(ktxTexture2* This)2021{2022if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_ETC1S)2023return true;2024else if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_UASTC)2025return true;2026else2027return false;2028}20292030#if KTX_FEATURE_WRITE2031/*2032* @memberof ktxTexture22033* @ingroup writer2034* @~English2035* @brief Set the transfer function for the images in a texture.2036*2037* @param[in] This pointer to the ktxTexture22038* @param[in] tf enumerator of the transfer function to set2039*2040* @return KTX_SUCCESS on success, other KTX_* enum values on error.2041*2042* @exception KTX_INVALID_OPERATION The transfer function is not valid for the2043* vkFormat of the texture.2044* @exception KTX_INVALID_VALUE The transfer function is not allowed by the2045* KTX spec.2046*/2047ktx_error_code_e2048ktxTexture2_SetTransferFunction(ktxTexture2* This, khr_df_transfer_e tf)2049{2050if (isSrgbFormat(This->vkFormat) && tf != KHR_DF_TRANSFER_SRGB)2051return KTX_INVALID_OPERATION;20522053if (isNotSrgbFormatButHasSrgbVariant(This->vkFormat) && tf == KHR_DF_TRANSFER_SRGB)2054return KTX_INVALID_OPERATION;20552056KHR_DFDSETVAL(This->pDfd + 1, TRANSFER, tf);2057return KTX_SUCCESS;2058}20592060/**2061* @memberof ktxTexture22062* @ingroup writer2063* @~English2064* @brief Set the transfer function for the images in a texture.2065* @deprecated Use ktxTexture2\_SetTransferFunction.2066*2067* @param[in] This pointer to the ktxTexture22068* @param[in] tf enumerator of the transfer function to set2069*/2070ktx_error_code_e2071ktxTexture2_SetOETF(ktxTexture2* This, khr_df_transfer_e tf)2072{2073return ktxTexture2_SetTransferFunction(This, tf);2074}20752076/**2077* @memberof ktxTexture22078* @ingroup writer2079* @~English2080* @brief Set the primaries for the images in a texture.2081*2082* @param[in] This pointer to the ktxTexture22083* @param[in] primaries enumerator of the primaries to set2084*/2085ktx_error_code_e2086ktxTexture2_SetPrimaries(ktxTexture2* This, khr_df_primaries_e primaries)2087{2088KHR_DFDSETVAL(This->pDfd + 1, PRIMARIES, primaries);2089return KTX_SUCCESS;2090}2091#endif20922093/**2094* @memberof ktxTexture22095* @~English2096* @brief Return the total size in bytes of the uncompressed data of a2097* ktxTexture2.2098*2099* If supercompressionScheme == @c KTX_SS_NONE or2100* @c KTX_SS_BASIS_LZ, returns the value of @c This->dataSize2101* else if supercompressionScheme == @c KTX_SS_ZSTD or @c KTX_SS_ZLIB, it2102* returns the sum of the uncompressed sizes of each mip level plus space for2103* the level padding. With no supercompression the data size and uncompressed2104* data size are the same. For Basis supercompression the uncompressed size2105* cannot be known until the data is transcoded so the compressed size is2106* returned.2107*2108* @param[in] This pointer to the ktxTexture1 object of interest.2109*/2110ktx_size_t2111ktxTexture2_GetDataSizeUncompressed(ktxTexture2* This)2112{2113switch (This->supercompressionScheme) {2114case KTX_SS_BASIS_LZ:2115case KTX_SS_NONE:2116return This->dataSize;2117case KTX_SS_ZSTD:2118case KTX_SS_ZLIB:2119{2120ktx_size_t uncompressedSize = 0;2121ktx_uint32_t uncompressedLevelAlignment;2122ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;21232124uncompressedLevelAlignment =2125ktxTexture2_calcPostInflationLevelAlignment(This);21262127for (ktx_int32_t level = This->numLevels - 1; level >= 1; level--) {2128ktx_size_t uncompressedLevelSize;2129uncompressedLevelSize = levelIndex[level].uncompressedByteLength;2130uncompressedLevelSize = _KTX_PADN(uncompressedLevelAlignment,2131uncompressedLevelSize);2132uncompressedSize += uncompressedLevelSize;2133}2134uncompressedSize += levelIndex[0].uncompressedByteLength;2135return uncompressedSize;2136}2137case KTX_SS_BEGIN_VENDOR_RANGE:2138case KTX_SS_END_VENDOR_RANGE:2139case KTX_SS_BEGIN_RESERVED:2140default:2141return 0;2142}2143}21442145/**2146* @memberof ktxTexture22147* @~English2148* @brief Calculate & return the size in bytes of an image at the specified2149* mip level.2150*2151* For arrays, this is the size of a layer, for cubemaps, the size of a face2152* and for 3D textures, the size of a depth slice.2153*2154* The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.2155*2156* @param[in] This pointer to the ktxTexture2 object of interest.2157* @param[in] level level of interest. *2158*/2159ktx_size_t2160ktxTexture2_GetImageSize(ktxTexture2* This, ktx_uint32_t level)2161{2162return ktxTexture_calcImageSize(ktxTexture(This), level,2163KTX_FORMAT_VERSION_TWO);2164}21652166/**2167* @memberof ktxTexture22168* @~English2169* @brief Calculate & return the size in bytes of all the images in the specified2170* mip level.2171*2172* For arrays, this is the size of all layers in the level, for cubemaps, the size of all2173* faces in the level and for 3D textures, the size of all depth slices in the level.2174*2175* @param[in] This pointer to the ktxTexture2 object of interest.2176* @param[in] level level of interest. *2177*/2178ktx_size_t2179ktxTexture2_GetLevelSize(ktxTexture2* This, ktx_uint32_t level)2180{2181return ktxTexture_calcLevelSize(ktxTexture(This), level,2182KTX_FORMAT_VERSION_TWO);2183}21842185/**2186* @memberof ktxTexture22187* @~English2188* @brief Iterate over the mip levels in a ktxTexture2 object.2189*2190* This is almost identical to ktxTexture_IterateLevelFaces(). The difference is2191* that the blocks of image data for non-array cube maps include all faces of2192* a mip level.2193*2194* This function works even if @p This->pData == 0 so it can be used to2195* obtain offsets and sizes for each level by callers who have loaded the data2196* externally.2197*2198* Intended for use only when supercompressionScheme == SUPERCOMPRESSION_NONE.2199*2200* @param[in] This handle of the ktxTexture opened on the data.2201* @param[in,out] iterCb the address of a callback function which is called2202* with the data for each image block.2203* @param[in,out] userdata the address of application-specific data which is2204* passed to the callback along with the image data.2205*2206* @return KTX_SUCCESS on success, other KTX_* enum values on error. The2207* following are returned directly by this function. @p iterCb may2208* return these for other causes or may return additional errors.2209*2210* @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not2211* decreasing2212* @exception KTX_INVALID_OPERATION supercompressionScheme != SUPERCOMPRESSION_NONE.2213* @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.2214*2215*/2216KTX_error_code2217ktxTexture2_IterateLevels(ktxTexture2* This, PFNKTXITERCB iterCb, void* userdata)2218{2219KTX_error_code result = KTX_SUCCESS;2220//ZSTD_DCtx* dctx;2221//ktx_uint8_t* decompBuf;2222ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;22232224if (This == NULL)2225return KTX_INVALID_VALUE;22262227if (iterCb == NULL)2228return KTX_INVALID_VALUE;22292230if (This->supercompressionScheme != KTX_SS_NONE)2231return KTX_INVALID_OPERATION;22322233for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)2234{2235ktx_uint32_t width, height, depth;2236ktx_uint64_t levelSize;2237ktx_uint64_t offset;22382239/* Array textures have the same number of layers at each mip level. */2240width = MAX(1, This->baseWidth >> level);2241height = MAX(1, This->baseHeight >> level);2242depth = MAX(1, This->baseDepth >> level);22432244levelSize = levelIndex[level].uncompressedByteLength;2245offset = ktxTexture2_levelDataOffset(This, level);22462247/* All array layers are passed in a group because that is how2248* GL & Vulkan need them. Hence no2249* for (layer = 0; layer < This->numLayers)2250*/2251result = iterCb(level, 0, width, height, depth,2252levelSize, This->pData + offset, userdata);2253if (result != KTX_SUCCESS)2254break;2255}22562257return result;2258}22592260/**2261* @memberof ktxTexture22262* @~English2263* @brief Iterate over the images in a ktxTexture2 object while loading the2264* image data.2265*2266* This operates similarly to ktxTexture_IterateLevelFaces() except that it2267* loads the images from the ktxTexture2's source to a temporary buffer2268* while iterating. If supercompressionScheme == KTX_SS_ZSTD or KTX_SS_ZLIB,2269* it will inflate the data before passing it to the callback. The callback function2270* must copy the image data if it wishes to preserve it as the temporary buffer2271* is reused for each level and is freed when this function exits.2272*2273* This function is helpful for reducing memory usage when uploading the data2274* to a graphics API.2275*2276* Intended for use only when supercompressionScheme == KTX_SS_NONE,2277* KTX_SS_ZSTD or KTX_SS_ZLIB. As there is no access to the ktxTexture's data on2278* conclusion of this function, destroying the texture on completion is recommended.2279*2280* @param[in] This pointer to the ktxTexture2 object of interest.2281* @param[in,out] iterCb the address of a callback function which is called2282* with the data for each image.2283* @param[in,out] userdata the address of application-specific data which is2284* passed to the callback along with the image data.2285*2286* @return KTX_SUCCESS on success, other KTX_* enum values on error. The2287* following are returned directly by this function. @p iterCb may2288* return these for other causes or may return additional errors.2289*2290* @exception KTX_FILE_DATA_ERROR mip level sizes are increasing not2291* decreasing2292* @exception KTX_INVALID_OPERATION the ktxTexture2 was not created from a2293* stream, i.e there is no data to load, or2294* this ktxTexture2's images have already2295* been loaded.2296* @exception KTX_INVALID_OPERATION2297* supercompressionScheme != KTX_SS_NONE,2298* supercompressionScheme != KTX_SS_ZSTD, and2299* supercompressionScheme != KTX_SS_ZLIB.2300* @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.2301* @exception KTX_OUT_OF_MEMORY not enough memory to allocate a block to2302* hold the base level image.2303*/2304KTX_error_code2305ktxTexture2_IterateLoadLevelFaces(ktxTexture2* This, PFNKTXITERCB iterCb,2306void* userdata)2307{2308DECLARE_PROTECTED(ktxTexture);2309ktxStream* stream = (ktxStream *)&prtctd->_stream;2310ktxLevelIndexEntry* levelIndex;2311ktx_size_t dataSize = 0, uncompressedDataSize = 0;2312KTX_error_code result = KTX_SUCCESS;2313ktx_uint8_t* dataBuf = NULL;2314ktx_uint8_t* uncompressedDataBuf = NULL;2315ktx_uint8_t* pData;2316ZSTD_DCtx* dctx = NULL;23172318if (This == NULL)2319return KTX_INVALID_VALUE;23202321if (This->classId != ktxTexture2_c)2322return KTX_INVALID_OPERATION;23232324if (This->supercompressionScheme != KTX_SS_NONE &&2325This->supercompressionScheme != KTX_SS_ZSTD &&2326This->supercompressionScheme != KTX_SS_ZLIB)2327return KTX_INVALID_OPERATION;23282329if (iterCb == NULL)2330return KTX_INVALID_VALUE;23312332if (prtctd->_stream.data.file == NULL)2333// This Texture not created from a stream or images are already loaded.2334return KTX_INVALID_OPERATION;23352336levelIndex = This->_private->_levelIndex;23372338// Allocate memory sufficient for the base level2339dataSize = levelIndex[0].byteLength;2340dataBuf = malloc(dataSize);2341if (!dataBuf)2342return KTX_OUT_OF_MEMORY;2343if (This->supercompressionScheme == KTX_SS_ZSTD || This->supercompressionScheme == KTX_SS_ZLIB) {2344uncompressedDataSize = levelIndex[0].uncompressedByteLength;2345uncompressedDataBuf = malloc(uncompressedDataSize);2346if (!uncompressedDataBuf) {2347result = KTX_OUT_OF_MEMORY;2348goto cleanup;2349}2350if (This->supercompressionScheme == KTX_SS_ZSTD) {2351dctx = ZSTD_createDCtx();2352}2353pData = uncompressedDataBuf;2354} else {2355pData = dataBuf;2356}23572358for (ktx_int32_t level = This->numLevels - 1; level >= 0; --level)2359{2360ktx_size_t levelSize;2361GLsizei width, height, depth;23622363// Array textures have the same number of layers at each mip level.2364width = MAX(1, This->baseWidth >> level);2365height = MAX(1, This->baseHeight >> level);2366depth = MAX(1, This->baseDepth >> level);23672368levelSize = levelIndex[level].byteLength;2369if (dataSize < levelSize) {2370// Levels cannot be larger than the base level2371result = KTX_FILE_DATA_ERROR;2372goto cleanup;2373}23742375// Use setpos so we skip any padding.2376result = stream->setpos(stream,2377ktxTexture2_levelFileOffset(This, level));2378if (result != KTX_SUCCESS)2379goto cleanup;23802381result = stream->read(stream, dataBuf, levelSize);2382if (result != KTX_SUCCESS)2383goto cleanup;23842385if (This->supercompressionScheme == KTX_SS_ZSTD) {2386levelSize =2387ZSTD_decompressDCtx(dctx, uncompressedDataBuf,2388uncompressedDataSize,2389dataBuf,2390levelSize);2391if (ZSTD_isError(levelSize)) {2392ZSTD_ErrorCode error = ZSTD_getErrorCode(levelSize);2393switch(error) {2394case ZSTD_error_dstSize_tooSmall:2395result = KTX_DECOMPRESS_LENGTH_ERROR; // inflatedDataCapacity too small.2396goto cleanup;2397case ZSTD_error_checksum_wrong:2398result = KTX_DECOMPRESS_CHECKSUM_ERROR;2399goto cleanup;2400case ZSTD_error_memory_allocation:2401result = KTX_OUT_OF_MEMORY;2402goto cleanup;2403default:2404result = KTX_FILE_DATA_ERROR;2405goto cleanup;2406}2407}24082409// We don't fix up the texture's dataSize, levelIndex or2410// _requiredAlignment because after this function completes there2411// is no way to get at the texture's data.2412//nindex[level].byteOffset = levelOffset;2413//nindex[level].uncompressedByteLength = nindex[level].byteLength =2414//levelByteLength;2415} else if (This->supercompressionScheme == KTX_SS_ZLIB) {2416result = ktxUncompressZLIBInt(uncompressedDataBuf,2417&uncompressedDataSize,2418dataBuf,2419levelSize);2420if (result != KTX_SUCCESS)2421return result;2422}24232424if (levelIndex[level].uncompressedByteLength != levelSize) {2425result = KTX_DECOMPRESS_LENGTH_ERROR;2426goto cleanup;2427}242824292430#if IS_BIG_ENDIAN2431switch (prtctd->_typeSize) {2432case 2:2433_ktxSwapEndian16((ktx_uint16_t*)pData, levelSize / 2);2434break;2435case 4:2436_ktxSwapEndian32((ktx_uint32_t*)pDest, levelSize / 4);2437break;2438case 8:2439_ktxSwapEndian64((ktx_uint64_t*)pDest, levelSize / 8);2440break;2441}2442#endif24432444// With the exception of non-array cubemaps the entire level2445// is passed at once because that is how OpenGL and Vulkan need them.2446// Vulkan could take all the faces at once too but we iterate2447// them separately for OpenGL.2448if (This->isCubemap && !This->isArray) {2449ktx_uint8_t* pFace = pData;2450struct blockCount {2451ktx_uint32_t x, y;2452} blockCount;2453ktx_size_t faceSize;24542455blockCount.x2456= (uint32_t)ceilf((float)width / prtctd->_formatSize.blockWidth);2457blockCount.y2458= (uint32_t)ceilf((float)height / prtctd->_formatSize.blockHeight);2459blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x);2460blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y);2461faceSize = blockCount.x * blockCount.y2462* prtctd->_formatSize.blockSizeInBits / 8;24632464for (ktx_uint32_t face = 0; face < This->numFaces; ++face) {2465result = iterCb(level, face,2466width, height, depth,2467(ktx_uint32_t)faceSize, pFace, userdata);2468pFace += faceSize;2469if (result != KTX_SUCCESS)2470goto cleanup;2471}2472} else {2473result = iterCb(level, 0,2474width, height, depth,2475(ktx_uint32_t)levelSize, pData, userdata);2476if (result != KTX_SUCCESS)2477goto cleanup;2478}2479}24802481// No further need for this.2482stream->destruct(stream);2483This->_private->_firstLevelFileOffset = 0;2484cleanup:2485free(dataBuf);2486if (uncompressedDataBuf) free(uncompressedDataBuf);2487if (dctx) ZSTD_freeDCtx(dctx);24882489return result;2490}24912492KTX_error_code2493ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,2494ktx_uint8_t* pInflatedData,2495ktx_size_t inflatedDataCapacity);24962497KTX_error_code2498ktxTexture2_inflateZLIBInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,2499ktx_uint8_t* pInflatedData,2500ktx_size_t inflatedDataCapacity);25012502typedef enum {2503LOADDATA_DONT_INFLATE_ON_LOAD,2504LOADDATA_INFLATE_ON_LOAD2505} ktxTexture2InflateFlagEnum;25062507/**2508* @memberof ktxTexture22509* @internal2510* @~English2511* @brief Load all the image data from the ktxTexture2's source.2512*2513* The data will be inflated if requested and supercompressionScheme == @c KTX_SS_ZSTD2514* or @c KTX_SS_ZLIB.2515* The data is loaded into the provided buffer or to an internally allocated2516* buffer, if @p pBuffer is @c NULL. Callers providing their own buffer must2517* ensure the buffer large enough to hold the inflated data for files deflated2518* with Zstd or ZLIB. See ktxTexture2\_GetDataSizeUncompressed().2519*2520* The texture's levelIndex, dataSize, DFD and supercompressionScheme will2521* all be updated after successful inflation to reflect the inflated data.2522*2523* @param[in] This pointer to the ktxTexture object of interest.2524* @param[in] pBuffer pointer to the buffer in which to load the image data.2525* @param[in] bufSize size of the buffer pointed at by @p pBuffer.2526* @param[in] inflateHandling enum indicating whether or not to inflate2527* supercompressed data.2528*2529* @return KTX_SUCCESS on success, other KTX_* enum values on error.2530*2531* @exception KTX_INVALID_VALUE @p This is NULL.2532* @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.2533* @exception KTX_INVALID_OPERATION2534* The data has already been loaded or the2535* ktxTexture was not created from a KTX source.2536* @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.2537*/2538ktx_error_code_e2539ktxTexture2_loadImageDataInt(ktxTexture2* This,2540ktx_uint8_t* pBuffer, ktx_size_t bufSize,2541ktxTexture2InflateFlagEnum inflateHandling)2542{2543DECLARE_PROTECTED(ktxTexture);2544DECLARE_PRIVATE(ktxTexture2);2545ktx_uint8_t* pDest;2546ktx_uint8_t* pDeflatedData = NULL;2547ktx_uint8_t* pReadBuf;2548KTX_error_code result = KTX_SUCCESS;2549ktx_size_t outputDataCapacity;2550ktx_bool_t doInflate = false;25512552if (This == NULL)2553return KTX_INVALID_VALUE;25542555if (This->pData != NULL)2556return KTX_INVALID_OPERATION; // Data already loaded.25572558if (prtctd->_stream.data.file == NULL)2559// This Texture not created from a stream or images already loaded;2560return KTX_INVALID_OPERATION;25612562if (inflateHandling == LOADDATA_INFLATE_ON_LOAD) {2563outputDataCapacity = ktxTexture2_GetDataSizeUncompressed(This);2564if (This->supercompressionScheme == KTX_SS_ZSTD || This->supercompressionScheme == KTX_SS_ZLIB)2565doInflate = true;2566} else {2567outputDataCapacity = This->dataSize;2568}25692570if (pBuffer == NULL) {2571This->pData = malloc(outputDataCapacity);2572if (This->pData == NULL)2573return KTX_OUT_OF_MEMORY;2574pDest = This->pData;2575} else if (bufSize < outputDataCapacity) {2576return KTX_INVALID_VALUE;2577} else {2578pDest = pBuffer;2579}25802581if (doInflate) {2582// Create buffer to hold deflated data.2583pDeflatedData = malloc(This->dataSize);2584if (pDeflatedData == NULL)2585return KTX_OUT_OF_MEMORY;2586pReadBuf = pDeflatedData;2587} else {2588pReadBuf = pDest;2589}25902591// Seek to data for first level as there may be padding between the2592// metadata/sgd and the image data.25932594result = prtctd->_stream.setpos(&prtctd->_stream,2595private->_firstLevelFileOffset);2596if (result != KTX_SUCCESS)2597goto cleanup;25982599result = prtctd->_stream.read(&prtctd->_stream, pReadBuf,2600This->dataSize);2601if (result != KTX_SUCCESS)2602goto cleanup;26032604if (doInflate) {2605assert(pDeflatedData != NULL);2606if (This->supercompressionScheme == KTX_SS_ZSTD) {2607result = ktxTexture2_inflateZstdInt(This, pDeflatedData, pDest,2608outputDataCapacity);2609} else if (This->supercompressionScheme == KTX_SS_ZLIB) {2610result = ktxTexture2_inflateZLIBInt(This, pDeflatedData, pDest,2611outputDataCapacity);2612}2613if (result != KTX_SUCCESS) {2614if (pBuffer == NULL) {2615free(This->pData);2616This->pData = 0;2617}2618goto cleanup;2619}2620}26212622if (IS_BIG_ENDIAN) {2623// Perform endianness conversion on texture data.2624// To avoid mip padding, need to convert each level individually.2625for (ktx_uint32_t level = 0; level < This->numLevels; ++level)2626{2627ktx_size_t levelOffset;2628ktx_size_t levelByteLength;26292630levelByteLength = private->_levelIndex[level].byteLength;2631levelOffset = ktxTexture2_levelDataOffset(This, level);2632pDest = This->pData + levelOffset;2633switch (prtctd->_typeSize) {2634case 2:2635_ktxSwapEndian16((ktx_uint16_t*)pDest, levelByteLength / 2);2636break;2637case 4:2638_ktxSwapEndian32((ktx_uint32_t*)pDest, levelByteLength / 4);2639break;2640case 8:2641_ktxSwapEndian64((ktx_uint64_t*)pDest, levelByteLength / 8);2642break;2643}2644}2645}26462647// No further need for stream or file offset.2648prtctd->_stream.destruct(&prtctd->_stream);2649private->_firstLevelFileOffset = 0;26502651cleanup:2652free(pDeflatedData);26532654return result;2655}26562657/**2658* @memberof ktxTexture22659* @~English2660* @brief Load all the image data from the ktxTexture2's source.2661*2662* The data will be inflated if supercompressionScheme == @c KTX_SS_ZSTD or2663* @c KTX_SS_ZLIB.2664* The data is loaded into the provided buffer or to an internally allocated2665* buffer, if @p pBuffer is @c NULL. Callers providing their own buffer must2666* ensure the buffer large enough to hold the inflated data for files deflated2667* with Zstd or ZLIB. See ktxTexture2\_GetDataSizeUncompressed().2668*2669* The texture's levelIndex, dataSize, DFD and supercompressionScheme will2670* all be updated after successful inflation to reflect the inflated data.2671*2672* @param[in] This pointer to the ktxTexture object of interest.2673* @param[in] pBuffer pointer to the buffer in which to load the image data.2674* @param[in] bufSize size of the buffer pointed at by @p pBuffer.2675*2676* @return KTX_SUCCESS on success, other KTX_* enum values on error.2677*2678* @exception KTX_INVALID_VALUE @p This is NULL.2679* @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.2680* @exception KTX_INVALID_OPERATION2681* The data has already been loaded or the2682* ktxTexture was not created from a KTX source.2683* @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.2684*/2685ktx_error_code_e2686ktxTexture2_LoadImageData(ktxTexture2* This,2687ktx_uint8_t* pBuffer, ktx_size_t bufSize)2688{2689return ktxTexture2_loadImageDataInt(This, pBuffer, bufSize, LOADDATA_INFLATE_ON_LOAD);2690}26912692/**2693* @memberof ktxTexture22694* @~English2695* @brief Load all the image data from the ktxTexture2's source without inflatiion..2696*2697* The data will be not be inflated if supercompressionScheme == @c KTX_SS_ZSTD or2698* @c KTX_SS_ZLIB. This function is provided to support some rare testing scenarios.2699* Generally use of ktxTexture2\_LoadImageData is highly recommended. For supercompressionScheme2700* values other than those mentioned, the result of this function is the same as2701* ktxTexture2\_LoadImageData.2702*2703* The data is loaded into the provided buffer or to an internally allocated2704* buffer, if @p pBuffer is @c NULL.2705*2706* @param[in] This pointer to the ktxTexture object of interest.2707* @param[in] pBuffer pointer to the buffer in which to load the image data.2708* @param[in] bufSize size of the buffer pointed at by @p pBuffer.2709*2710* @return KTX_SUCCESS on success, other KTX_* enum values on error.2711*2712* @exception KTX_INVALID_VALUE @p This is NULL.2713* @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.2714* @exception KTX_INVALID_OPERATION2715* The data has already been loaded or the2716* ktxTexture was not created from a KTX source.2717* @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.2718*/2719ktx_error_code_e2720ktxTexture2_LoadDeflatedImageData(ktxTexture2* This,2721ktx_uint8_t* pBuffer, ktx_size_t bufSize)2722{2723return ktxTexture2_loadImageDataInt(This, pBuffer, bufSize, LOADDATA_DONT_INFLATE_ON_LOAD);2724}27252726/**2727* @memberof ktxTexture2 @private2728* @~English2729* @brief Retrieve the offset of a level's first image within the ktxTexture2's2730* image data.2731*2732* @param[in] This pointer to the ktxTexture2 object of interest.2733*/2734ktx_uint64_t ktxTexture2_levelDataOffset(ktxTexture2* This, ktx_uint32_t level)2735{2736return This->_private->_levelIndex[level].byteOffset;2737}27382739/**2740* @memberof ktxTexture2 @private2741* @~English2742* @brief Inflate the data in a ktxTexture2 object using Zstandard.2743*2744* The texture's levelIndex, dataSize, DFD, data pointer, and supercompressionScheme will2745* all be updated after successful inflation to reflect the inflated data.2746*2747* @param[in] This pointer to the ktxTexture2 object of interest.2748* @param[in] pDeflatedData pointer to a buffer containing the deflated data2749* of the entire texture.2750* @param[in,out] pInflatedData pointer to a buffer in which to write the inflated2751* data.2752* @param[in] inflatedDataCapacity capacity of the buffer pointed at by2753* @p pInflatedData.2754*/2755KTX_error_code2756ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,2757ktx_uint8_t* pInflatedData,2758ktx_size_t inflatedDataCapacity)2759{2760ktx_uint32_t levelIndexByteLength =2761This->numLevels * sizeof(ktxLevelIndexEntry);2762uint64_t levelOffset = 0;2763ktxLevelIndexEntry* cindex = This->_private->_levelIndex;2764ktxLevelIndexEntry* nindex = NULL;2765ktx_uint32_t uncompressedLevelAlignment;2766ktx_error_code_e result = KTX_SUCCESS;27672768ZSTD_DCtx* dctx = NULL;27692770if (pDeflatedData == NULL)2771return KTX_INVALID_VALUE;27722773if (pInflatedData == NULL)2774return KTX_INVALID_VALUE;27752776if (This->supercompressionScheme != KTX_SS_ZSTD)2777return KTX_INVALID_OPERATION;27782779nindex = malloc(levelIndexByteLength);2780if (nindex == NULL)2781return KTX_OUT_OF_MEMORY;27822783uncompressedLevelAlignment =2784ktxTexture2_calcPostInflationLevelAlignment(This);27852786ktx_size_t inflatedByteLength = 0;2787dctx = ZSTD_createDCtx();2788if (dctx == NULL) {2789result = KTX_OUT_OF_MEMORY;2790goto cleanup;2791}2792for (int32_t level = This->numLevels - 1; level >= 0; level--) {2793size_t levelByteLength =2794ZSTD_decompressDCtx(dctx, pInflatedData + levelOffset,2795inflatedDataCapacity,2796&pDeflatedData[cindex[level].byteOffset],2797cindex[level].byteLength);2798if (ZSTD_isError(levelByteLength)) {2799ZSTD_ErrorCode error = ZSTD_getErrorCode(levelByteLength);2800switch(error) {2801case ZSTD_error_dstSize_tooSmall:2802result = KTX_DECOMPRESS_LENGTH_ERROR; // inflatedDataCapacity too small.2803goto cleanup;2804case ZSTD_error_checksum_wrong:2805result = KTX_DECOMPRESS_CHECKSUM_ERROR;2806goto cleanup;2807case ZSTD_error_memory_allocation:2808result = KTX_OUT_OF_MEMORY;2809goto cleanup;2810default:2811result = KTX_FILE_DATA_ERROR;2812goto cleanup;2813}2814}28152816if (This->_private->_levelIndex[level].uncompressedByteLength != levelByteLength) {2817result = KTX_DECOMPRESS_LENGTH_ERROR;2818goto cleanup;2819}28202821nindex[level].byteOffset = levelOffset;2822nindex[level].uncompressedByteLength = nindex[level].byteLength =2823levelByteLength;2824ktx_size_t paddedLevelByteLength2825= _KTX_PADN(uncompressedLevelAlignment, levelByteLength);2826inflatedByteLength += paddedLevelByteLength;2827levelOffset += paddedLevelByteLength;2828inflatedDataCapacity -= paddedLevelByteLength;2829}28302831// Now modify the texture.28322833This->dataSize = inflatedByteLength;2834This->supercompressionScheme = KTX_SS_NONE;2835memcpy(cindex, nindex, levelIndexByteLength); // Update level index2836This->_private->_requiredLevelAlignment = uncompressedLevelAlignment;28372838cleanup:2839ZSTD_freeDCtx(dctx);2840free(nindex);2841return result;2842}28432844/**2845* @memberof ktxTexture2 @private2846* @~English2847* @brief Inflate the data in a ktxTexture2 object using miniz (ZLIB).2848*2849* The texture's levelIndex, dataSize, DFD, data pointer, and supercompressionScheme will2850* all be updated after successful inflation to reflect the inflated data.2851*2852* @param[in] This pointer to the ktxTexture2 object of interest.2853* @param[in] pDeflatedData pointer to a buffer containing the deflated2854* data of the entire texture.2855* @param[in,out] pInflatedData pointer to a buffer in which to write the2856* inflated data.2857* @param[in] inflatedDataCapacity capacity of the buffer pointed at by2858* @p pInflatedData.2859*/2860KTX_error_code2861ktxTexture2_inflateZLIBInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,2862ktx_uint8_t* pInflatedData,2863ktx_size_t inflatedDataCapacity)2864{2865ktx_uint32_t levelIndexByteLength =2866This->numLevels * sizeof(ktxLevelIndexEntry);2867uint64_t levelOffset = 0;2868ktxLevelIndexEntry* cindex = This->_private->_levelIndex;2869ktxLevelIndexEntry* nindex;2870ktx_uint32_t uncompressedLevelAlignment;28712872if (pDeflatedData == NULL)2873return KTX_INVALID_VALUE;28742875if (pInflatedData == NULL)2876return KTX_INVALID_VALUE;28772878if (This->supercompressionScheme != KTX_SS_ZLIB)2879return KTX_INVALID_OPERATION;28802881nindex = malloc(levelIndexByteLength);2882if (nindex == NULL)2883return KTX_OUT_OF_MEMORY;28842885uncompressedLevelAlignment =2886ktxTexture2_calcPostInflationLevelAlignment(This);28872888ktx_size_t inflatedByteLength = 0;2889for (int32_t level = This->numLevels - 1; level >= 0; level--) {2890size_t levelByteLength = inflatedDataCapacity;2891KTX_error_code result = ktxUncompressZLIBInt(pInflatedData + levelOffset,2892&levelByteLength,2893&pDeflatedData[cindex[level].byteOffset],2894cindex[level].byteLength);2895if (result != KTX_SUCCESS) {2896free(nindex);2897return result;2898}28992900if (This->_private->_levelIndex[level].uncompressedByteLength != levelByteLength) {2901free(nindex);2902return KTX_DECOMPRESS_LENGTH_ERROR;2903}29042905nindex[level].byteOffset = levelOffset;2906nindex[level].uncompressedByteLength = nindex[level].byteLength =2907levelByteLength;2908ktx_size_t paddedLevelByteLength2909= _KTX_PADN(uncompressedLevelAlignment, levelByteLength);2910inflatedByteLength += paddedLevelByteLength;2911levelOffset += paddedLevelByteLength;2912inflatedDataCapacity -= paddedLevelByteLength;2913}29142915// Now modify the texture.29162917This->dataSize = inflatedByteLength;2918This->supercompressionScheme = KTX_SS_NONE;2919memcpy(cindex, nindex, levelIndexByteLength); // Update level index2920free(nindex);2921This->_private->_requiredLevelAlignment = uncompressedLevelAlignment;29222923return KTX_SUCCESS;2924}29252926#if !KTX_FEATURE_WRITE29272928/*2929* Stubs for writer functions that return a proper error code2930*/29312932KTX_error_code2933ktxTexture2_SetImageFromMemory(ktxTexture2* This, ktx_uint32_t level,2934ktx_uint32_t layer, ktx_uint32_t faceSlice,2935const ktx_uint8_t* src, ktx_size_t srcSize)2936{2937UNUSED(This);2938UNUSED(level);2939UNUSED(layer);2940UNUSED(faceSlice);2941UNUSED(src);2942UNUSED(srcSize);2943return KTX_INVALID_OPERATION;2944}29452946KTX_error_code2947ktxTexture2_SetImageFromStdioStream(ktxTexture2* This, ktx_uint32_t level,2948ktx_uint32_t layer, ktx_uint32_t faceSlice,2949FILE* src, ktx_size_t srcSize)2950{2951UNUSED(This);2952UNUSED(level);2953UNUSED(layer);2954UNUSED(faceSlice);2955UNUSED(src);2956UNUSED(srcSize);2957return KTX_INVALID_OPERATION;2958}29592960KTX_error_code2961ktxTexture2_WriteToStdioStream(ktxTexture2* This, FILE* dstsstr)2962{2963UNUSED(This);2964UNUSED(dstsstr);2965return KTX_INVALID_OPERATION;2966}29672968KTX_error_code2969ktxTexture2_WriteToNamedFile(ktxTexture2* This, const char* const dstname)2970{2971UNUSED(This);2972UNUSED(dstname);2973return KTX_INVALID_OPERATION;2974}29752976KTX_error_code2977ktxTexture2_WriteToMemory(ktxTexture2* This,2978ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)2979{2980UNUSED(This);2981UNUSED(ppDstBytes);2982UNUSED(pSize);2983return KTX_INVALID_OPERATION;2984}29852986KTX_error_code2987ktxTexture2_WriteToStream(ktxTexture2* This,2988ktxStream* dststr)2989{2990UNUSED(This);2991UNUSED(dststr);2992return KTX_INVALID_OPERATION;2993}29942995#endif29962997/*2998* Initialized here at the end to avoid the need for multiple declarations of2999* the virtual functions.3000*/30013002struct ktxTexture_vtblInt ktxTexture2_vtblInt = {3003(PFNCALCDATASIZELEVELS)ktxTexture2_calcDataSizeLevels,3004(PFNCALCFACELODSIZE)ktxTexture2_calcFaceLodSize,3005(PFNCALCLEVELOFFSET)ktxTexture2_calcLevelOffset3006};30073008struct ktxTexture_vtbl ktxTexture2_vtbl = {3009(PFNKTEXDESTROY)ktxTexture2_Destroy,3010(PFNKTEXGETIMAGEOFFSET)ktxTexture2_GetImageOffset,3011(PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture2_GetDataSizeUncompressed,3012(PFNKTEXGETIMAGESIZE)ktxTexture2_GetImageSize,3013(PFNKTEXGETLEVELSIZE)ktxTexture2_GetLevelSize,3014(PFNKTEXITERATELEVELS)ktxTexture2_IterateLevels,3015(PFNKTEXITERATELOADLEVELFACES)ktxTexture2_IterateLoadLevelFaces,3016(PFNKTEXNEEDSTRANSCODING)ktxTexture2_NeedsTranscoding,3017(PFNKTEXLOADIMAGEDATA)ktxTexture2_LoadImageData,3018(PFNKTEXSETIMAGEFROMMEMORY)ktxTexture2_SetImageFromMemory,3019(PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture2_SetImageFromStdioStream,3020(PFNKTEXWRITETOSTDIOSTREAM)ktxTexture2_WriteToStdioStream,3021(PFNKTEXWRITETONAMEDFILE)ktxTexture2_WriteToNamedFile,3022(PFNKTEXWRITETOMEMORY)ktxTexture2_WriteToMemory,3023(PFNKTEXWRITETOSTREAM)ktxTexture2_WriteToStream,3024};30253026/** @} */3027302830293030