/* -*- 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, github.com/MarkCallow16*/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;57// MSVC 14.44 introduced a warning when mixing enums of different types.58// To avoid doing that make separate channelId and qualifier fields.59uint32_t channelId : 4;60uint32_t datatypeQualifiers : 4;61uint32_t samplePosition0 : 8;62uint32_t samplePosition1: 8;63uint32_t samplePosition2: 8;64uint32_t samplePosition3: 8;65uint32_t lower;66uint32_t upper;67};6869struct BDFD {70uint32_t vendorId: 17;71uint32_t descriptorType: 15;72uint32_t versionNumber: 16;73uint32_t descriptorBlockSize: 16;74uint32_t model: 8;75uint32_t primaries: 8;76uint32_t transfer: 8;77uint32_t flags: 8;78uint32_t texelBlockDimension0: 8;79uint32_t texelBlockDimension1: 8;80uint32_t texelBlockDimension2: 8;81uint32_t texelBlockDimension3: 8;82uint32_t bytesPlane0: 8;83uint32_t bytesPlane1: 8;84uint32_t bytesPlane2: 8;85uint32_t bytesPlane3: 8;86uint32_t bytesPlane4: 8;87uint32_t bytesPlane5: 8;88uint32_t bytesPlane6: 8;89uint32_t bytesPlane7: 8;90struct sampleType samples[6];91};9293struct BDFD e5b9g9r9_ufloat_comparator = {94.vendorId = 0,95.descriptorType = 0,96.versionNumber = 2,97.descriptorBlockSize = sizeof(struct BDFD),98.model = KHR_DF_MODEL_RGBSDA,99.primaries = KHR_DF_PRIMARIES_BT709,100.transfer = KHR_DF_TRANSFER_LINEAR,101.flags = KHR_DF_FLAG_ALPHA_STRAIGHT,102.texelBlockDimension0 = 0,103.texelBlockDimension1 = 0,104.texelBlockDimension2 = 0,105.texelBlockDimension3 = 0,106.bytesPlane0 = 4,107.bytesPlane1 = 0,108.bytesPlane2 = 0,109.bytesPlane3 = 0,110.bytesPlane4 = 0,111.bytesPlane5 = 0,112.bytesPlane6 = 0,113.bytesPlane7 = 0,114// gcc likes this way. It does not like, e.g.,115// .samples[0].bitOffset = 0, etc. which is accepted by both clang & msvc.116// I find the standards docs impenetrable so I don't know which is correct.117.samples[0] = {118.bitOffset = 0,119.bitLength = 8,120.channelId = KHR_DF_CHANNEL_RGBSDA_RED,121.datatypeQualifiers = 0,122.samplePosition0 = 0,123.samplePosition1 = 0,124.samplePosition2 = 0,125.samplePosition3 = 0,126.lower = 0,127.upper = 8448,128},129.samples[1] = {130.bitOffset = 27,131.bitLength = 4,132.channelId = KHR_DF_CHANNEL_RGBSDA_RED,133// The constant is defined to be ORed with a channelId into134// an 8-bit value. Shift to make it suitable for the 4-bit field.135.datatypeQualifiers = (KHR_DF_SAMPLE_DATATYPE_EXPONENT >> 4U),136.samplePosition0 = 0,137.samplePosition1 = 0,138.samplePosition2 = 0,139.samplePosition3 = 0,140.lower = 15,141.upper = 31,142},143.samples[2] = {144.bitOffset = 9,145.bitLength = 8,146.channelId = KHR_DF_CHANNEL_RGBSDA_GREEN,147.datatypeQualifiers = 0,148.samplePosition0 = 0,149.samplePosition1 = 0,150.samplePosition2 = 0,151.samplePosition3 = 0,152.lower = 0,153.upper = 8448,154},155.samples[3] = {156.bitOffset = 27,157.bitLength = 4,158.channelId = KHR_DF_CHANNEL_RGBSDA_GREEN,159// Ditto comment in samples[1].160.datatypeQualifiers = (KHR_DF_SAMPLE_DATATYPE_EXPONENT >> 4U),161.samplePosition0 = 0,162.samplePosition1 = 0,163.samplePosition2 = 0,164.samplePosition3 = 0,165.lower = 15,166.upper = 31,167},168.samples[4] = {169.bitOffset = 18,170.bitLength = 8,171.channelId = KHR_DF_CHANNEL_RGBSDA_BLUE,172.datatypeQualifiers = 0,173.samplePosition0 = 0,174.samplePosition1 = 0,175.samplePosition2 = 0,176.samplePosition3 = 0,177.lower = 0,178.upper = 8448,179},180.samples[5] = {181.bitOffset = 27,182.bitLength = 4,183.channelId = KHR_DF_CHANNEL_RGBSDA_BLUE,184// Ditto comment in samples[1].185.datatypeQualifiers = (KHR_DF_SAMPLE_DATATYPE_EXPONENT >> 4U),186.samplePosition0 = 0,187.samplePosition1 = 0,188.samplePosition2 = 0,189.samplePosition3 = 0,190.lower = 15,191.upper = 31,192}193};194#else195// For compilers which order bitfields from the msb rather than lsb.196#define shift(x,val) ((val) << KHR_DF_SHIFT_ ## x)197#define sampleshift(x,val) ((val) << KHR_DF_SAMPLESHIFT_ ## x)198#define e5b9g9r9_bdbwordcount KHR_DFDSIZEWORDS(6)199ktx_uint32_t e5b9g9r9_ufloat_comparator[e5b9g9r9_bdbwordcount] = {2000, // descriptorType & vendorId201shift(DESCRIPTORBLOCKSIZE, e5b9g9r9_bdbwordcount * sizeof(ktx_uint32_t)) | shift(VERSIONNUMBER, 2),202// N.B. Allow various values of primaries, transfer & flags203shift(FLAGS, KHR_DF_FLAG_ALPHA_STRAIGHT) | shift(TRANSFER, KHR_DF_TRANSFER_LINEAR) | shift(PRIMARIES, KHR_DF_PRIMARIES_BT709) | shift(MODEL, KHR_DF_MODEL_RGBSDA),2040, // texelBlockDimension3~0205shift(BYTESPLANE0, 4), // All other bytesPlane fields are 0.2060, // bytesPlane7~4207sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 0),2080, // samplePosition3~02090, // sampleLower2108448, // sampleUpper211sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),2120, // samplePosition3~021315, // sampleLower21431, // sampleUpper215sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 9),2160, // samplePosition3~02170, // sampleLower2188448, // sampleUpper219sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),2200, // samplePosition3~022115, // sampleLower22231, // sampleUpper223sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 18),2240, // samplePosition3~02250, // sampleLower2268448, // sampleUpper227sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),2280, // samplePosition3~022915, // sampleLower23031, // sampleUpper231};232#endif233234/* Helper constant:235Minimal size of basic descriptor block to safely read its size */236#define KHR_DFD_SIZEFOR_DESCRIPTORBLOCKSIZE \237((KHR_DF_WORD_DESCRIPTORBLOCKSIZE + 1) * sizeof(uint32_t))238239/**240* @private241* @~English242* @brief Initialize a ktxFormatSize object from the info in a DFD.243*244* This is used instead of referring to the DFD directly so code dealing245* with format info can be common to KTX 1 & 2.246*247* @param[in] This pointer the ktxFormatSize to initialize.248* @param[in] pDFD pointer to the DFD whose data to use.249*250* @return KTX_TRUE on success, otherwise KTX_FALSE.251*/252bool253ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd)254{255uint32_t* pBdb = pDfd + 1;256// pDfd[0] contains totalSize in bytes, check if it has at least257// KHR_DFD_SIZEFOR_DESCRIPTORBLOCKSIZE bytes258if (pDfd[0] < KHR_DFD_SIZEFOR_DESCRIPTORBLOCKSIZE || *pBdb != 0) {259// Either decriptorType or vendorId is not 0260return false;261}262// Iterate through all block descriptors and check if sum of their sizes263// is equal to the totalSize in pDfd[0]264uint32_t descriptorSize = pDfd[0] - sizeof(uint32_t);265while(descriptorSize > KHR_DFD_SIZEFOR_DESCRIPTORBLOCKSIZE) {266uint32_t descriptorBlockSize = KHR_DFDVAL(pBdb, DESCRIPTORBLOCKSIZE);267if (descriptorBlockSize <= descriptorSize) {268descriptorSize -= descriptorBlockSize;269pBdb += descriptorBlockSize / sizeof(uint32_t);270} else {271break;272}273}274if (descriptorSize != 0) {275return false;276}277278// reset pBdb pointer to the first block descriptor279pBdb = pDfd + 1;280281// Check the DFD is of the expected version.282if (KHR_DFDVAL(pBdb, VERSIONNUMBER) != KHR_DF_VERSIONNUMBER_1_3) {283return false;284}285286// DFD has supported type and version. Process it.287This->blockWidth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION0) + 1;288This->blockHeight = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION1) + 1;289This->blockDepth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION2) + 1;290if (KHR_DFDVAL(pBdb, BYTESPLANE0) == 0) {291// The DFD uses the deprecated way of indicating a supercompressed292// texture. Reconstruct the original values.293reconstructDFDBytesPlanesFromSamples(pDfd);294}295This->blockSizeInBits = KHR_DFDVAL(pBdb, BYTESPLANE0) * 8;296// Account for ETC1S with possible second slice.297This->blockSizeInBits += KHR_DFDVAL(pBdb, BYTESPLANE1) * 8;298This->paletteSizeInBits = 0; // No paletted formats in ktx v2.299This->flags = 0;300This->minBlocksX = This->minBlocksY = 1;301if (KHR_DFDVAL(pBdb, MODEL) >= KHR_DF_MODEL_DXT1A) {302// A block compressed format. Entire block is a single sample.303This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT;304if (KHR_DFDVAL(pBdb, MODEL) == KHR_DF_MODEL_ETC1S) {305// Special case the only multi-plane format we handle.306This->blockSizeInBits += KHR_DFDVAL(pBdb, BYTESPLANE1) * 8;307}308if (KHR_DFDVAL(pBdb, MODEL) == KHR_DF_MODEL_PVRTC) {309This->minBlocksX = This->minBlocksY = 2;310}311} else {312// An uncompressed format.313314// Special case depth & depth stencil formats315if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_DEPTH) {316if (KHR_DFDSAMPLECOUNT(pBdb) == 1) {317This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;318} else if (KHR_DFDSAMPLECOUNT(pBdb) == 2) {319This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;320This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;321This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;322} else {323return false;324}325} else if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_STENCIL) {326This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;327} else if (KHR_DFDSAMPLECOUNT(pBdb) == 6328#if !defined(BITFIELD_ORDER_FROM_MSB)329&& !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))) {330#else331&& !memcmp(&e5b9g9r9_ufloat_comparator[KHR_DF_WORD_TEXELBLOCKDIMENSION0], &pBdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0], sizeof(e5b9g9r9_ufloat_comparator)-(KHR_DF_WORD_TEXELBLOCKDIMENSION0)*sizeof(uint32_t))) {332#endif333// Special case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 as interpretDFD334// only handles "simple formats", i.e. where channels are described335// in contiguous bits.336This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;337} else {338InterpretedDFDChannel rgba[4];339uint32_t wordBytes;340enum InterpretDFDResult result;341342result = interpretDFD(pDfd, &rgba[0], &rgba[1], &rgba[2], &rgba[3],343&wordBytes);344if (result >= i_UNSUPPORTED_ERROR_BIT)345return false;346if (result & i_PACKED_FORMAT_BIT)347This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;348if (result & i_COMPRESSED_FORMAT_BIT)349This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT;350if (result & i_YUVSDA_FORMAT_BIT)351This->flags |= KTX_FORMAT_SIZE_YUVSDA_BIT;352}353}354return true;355}356357/**358* @private359* @~English360* @brief Create a DFD for a VkFormat.361*362* This KTX-specific function adds support for combined depth stencil formats363* which are not supported by @e dfdutils' @c vk2dfd function because they364* are not seen outside a Vulkan device. KTX has its own definitions for365* these that enable uploading, with some effort.366*367* @param[in] vkFormat the format for which to create a DFD.368*/369static uint32_t*370ktxVk2dfd(ktx_uint32_t vkFormat)371{372return vk2dfd(vkFormat);373}374375/**376* @memberof ktxTexture2 @private377* @~English378* @brief Do the part of ktxTexture2 construction that is common to379* new textures and those constructed from a stream.380*381* @param[in] This pointer to a ktxTexture2-sized block of memory to382* initialize.383* @param[in] numLevels the number of levels the texture must have.384*385* @return KTX_SUCCESS on success, other KTX_* enum values on error.386* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.387*/388static KTX_error_code389ktxTexture2_constructCommon(ktxTexture2* This, ktx_uint32_t numLevels)390{391assert(This != NULL);392ktx_size_t privateSize;393394This->classId = ktxTexture2_c;395This->vtbl = &ktxTexture2_vtbl;396This->_protected->_vtbl = ktxTexture2_vtblInt;397privateSize = sizeof(ktxTexture2_private)398+ sizeof(ktxLevelIndexEntry) * (numLevels - 1);399This->_private = (ktxTexture2_private*)malloc(privateSize);400if (This->_private == NULL) {401return KTX_OUT_OF_MEMORY;402}403memset(This->_private, 0, privateSize);404return KTX_SUCCESS;405}406407/*408* In hindsight this function should have been `#if KTX_FEATURE_WRITE`.409* In the interest of not breaking an app that may be using this via410* `ktxTexture2_Create` in `libktx_read` we won't change it.411*/412/**413* @memberof ktxTexture2 @private414* @~English415* @brief Construct a new, empty, ktxTexture2.416*417* @param[in] This pointer to a ktxTexture2-sized block of memory to418* initialize.419* @param[in] createInfo pointer to a ktxTextureCreateInfo struct with420* information describing the texture.421* @param[in] storageAllocation422* enum indicating whether or not to allocate storage423* for the texture images.424* @return KTX_SUCCESS on success, other KTX_* enum values on error.425* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture or image data.426* @exception KTX_UNSUPPORTED_TEXTURE_TYPE427* The request VkFormat is one of the428* prohibited formats or is otherwise429* unsupported.430*/431static KTX_error_code432ktxTexture2_construct(ktxTexture2* This,433const ktxTextureCreateInfo* const createInfo,434ktxTextureCreateStorageEnum storageAllocation)435{436ktxFormatSize formatSize;437KTX_error_code result;438439memset(This, 0, sizeof(*This));440441if (createInfo->vkFormat != VK_FORMAT_UNDEFINED) {442if (isProhibitedFormat(createInfo->vkFormat))443return KTX_UNSUPPORTED_TEXTURE_TYPE;444This->pDfd = ktxVk2dfd(createInfo->vkFormat);445if (!This->pDfd)446return KTX_INVALID_VALUE; // Format is unknown or unsupported.447448#ifdef _DEBUG449// If this fires, an unsupported format or incorrect DFD450// has crept into vk2dfd.451assert(ktxFormatSize_initFromDfd(&formatSize, This->pDfd));452#else453(void)ktxFormatSize_initFromDfd(&formatSize, This->pDfd);454#endif455456} else {457// TODO: Validate createInfo->pDfd.458This->pDfd = (ktx_uint32_t*)malloc(*createInfo->pDfd);459if (!This->pDfd)460return KTX_OUT_OF_MEMORY;461memcpy(This->pDfd, createInfo->pDfd, *createInfo->pDfd);462if (!ktxFormatSize_initFromDfd(&formatSize, This->pDfd)) {463result = KTX_UNSUPPORTED_TEXTURE_TYPE;464goto cleanup;465}466}467468result = ktxTexture_construct(ktxTexture(This), createInfo, &formatSize);469470if (result != KTX_SUCCESS)471return result;472result = ktxTexture2_constructCommon(This, createInfo->numLevels);473if (result != KTX_SUCCESS)474goto cleanup;;475476This->vkFormat = createInfo->vkFormat;477478// The typeSize cannot be reconstructed just from the DFD as the BDFD479// does not capture the packing expressed by the [m]PACK[n] layout480// information in the VkFormat, so we calculate the typeSize directly481// from the vkFormat482This->_protected->_typeSize = vkFormatTypeSize(createInfo->vkFormat);483484This->supercompressionScheme = KTX_SS_NONE;485486This->_private->_requiredLevelAlignment487= ktxTexture2_calcRequiredLevelAlignment(This);488489// Create levelIndex. Offsets are from start of the KTX2 stream.490ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;491492This->_private->_firstLevelFileOffset = 0;493494for (ktx_uint32_t level = 0; level < This->numLevels; level++) {495levelIndex[level].uncompressedByteLength =496ktxTexture_calcLevelSize(ktxTexture(This), level,497KTX_FORMAT_VERSION_TWO);498levelIndex[level].byteLength =499levelIndex[level].uncompressedByteLength;500levelIndex[level].byteOffset =501ktxTexture_calcLevelOffset(ktxTexture(This), level);502}503504// Allocate storage, if requested.505if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) {506This->dataSize507= ktxTexture_calcDataSizeTexture(ktxTexture(This));508This->pData = malloc(This->dataSize);509if (This->pData == NULL) {510result = KTX_OUT_OF_MEMORY;511goto cleanup;512}513}514return result;515516cleanup:517ktxTexture2_destruct(This);518return result;519}520521/**522* @memberof ktxTexture2 @private523* @~English524* @brief Construct a ktxTexture by copying a source ktxTexture.525*526* @param[in] This pointer to a ktxTexture2-sized block of memory to527* initialize.528* @param[in] orig pointer to the source texture to copy.529*530* @return KTX_SUCCESS on success, other KTX_* enum values on error.531*532* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.533*/534KTX_error_code535ktxTexture2_constructCopy(ktxTexture2* This, ktxTexture2* orig)536{537KTX_error_code result;538539memcpy(This, orig, sizeof(ktxTexture2));540// Zero all the pointers to make error handling easier541This->_protected = NULL;542This->_private = NULL;543This->pDfd = NULL;544This->kvData = NULL;545This->kvDataHead = NULL;546This->pData = NULL;547548This->_protected =549(ktxTexture_protected*)malloc(sizeof(ktxTexture_protected));550if (!This->_protected)551return KTX_OUT_OF_MEMORY;552// Must come before memcpy of _protected so as to close an active stream.553if (!orig->pData && ktxTexture_isActiveStream((ktxTexture*)orig))554ktxTexture2_LoadImageData(orig, NULL, 0);555memcpy(This->_protected, orig->_protected, sizeof(ktxTexture_protected));556557ktx_size_t privateSize = sizeof(ktxTexture2_private)558+ sizeof(ktxLevelIndexEntry) * (orig->numLevels - 1);559This->_private = (ktxTexture2_private*)malloc(privateSize);560if (This->_private == NULL) {561result = KTX_OUT_OF_MEMORY;562goto cleanup;563}564memcpy(This->_private, orig->_private, privateSize);565if (orig->_private->_sgdByteLength > 0) {566This->_private->_supercompressionGlobalData567= (ktx_uint8_t*)malloc(orig->_private->_sgdByteLength);568if (!This->_private->_supercompressionGlobalData) {569result = KTX_OUT_OF_MEMORY;570goto cleanup;571}572memcpy(This->_private->_supercompressionGlobalData,573orig->_private->_supercompressionGlobalData,574orig->_private->_sgdByteLength);575}576577This->pDfd = (ktx_uint32_t*)malloc(*orig->pDfd);578if (!This->pDfd) {579result = KTX_OUT_OF_MEMORY;580goto cleanup;581}582memcpy(This->pDfd, orig->pDfd, *orig->pDfd);583584if (orig->kvDataHead) {585ktxHashList_ConstructCopy(&This->kvDataHead, orig->kvDataHead);586} else if (orig->kvData) {587This->kvData = (ktx_uint8_t*)malloc(orig->kvDataLen);588if (!This->kvData) {589result = KTX_OUT_OF_MEMORY;590goto cleanup;591}592memcpy(This->kvData, orig->kvData, orig->kvDataLen);593}594595// Can't share the image data as the data pointer is exposed in the596// ktxTexture2 structure. Changing it to a ref-counted pointer would597// break code. Maybe that's okay as we're still pre-release. But,598// since this constructor will be mostly be used when transcoding599// supercompressed images, it is probably not too big a deal to make600// a copy of the data.601This->pData = (ktx_uint8_t*)malloc(This->dataSize);602if (This->pData == NULL) {603result = KTX_OUT_OF_MEMORY;604goto cleanup;605}606memcpy(This->pData, orig->pData, orig->dataSize);607return KTX_SUCCESS;608609cleanup:610if (This->_protected) free(This->_protected);611if (This->_private) {612if (This->_private->_supercompressionGlobalData)613free(This->_private->_supercompressionGlobalData);614free(This->_private);615}616if (This->pDfd) free (This->pDfd);617if (This->kvDataHead) ktxHashList_Destruct(&This->kvDataHead);618619return result;620}621622bool isSrgbFormat(VkFormat format);623bool isNotSrgbFormatButHasSrgbVariant(VkFormat format);624625/**626* @memberof ktxTexture2 @private627* @~English628* @brief Construct a ktxTexture from a ktxStream reading from a KTX source.629*630* The KTX header, which must have been read prior to calling this, is passed631* to the function.632*633* The stream object is copied into the constructed ktxTexture2.634*635* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,636* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This637* will minimize memory usage by allowing, for example, loading the images638* directly from the source into a Vulkan staging buffer.639*640* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is641* provided solely to enable implementation of the @e libktx v1 API on top of642* ktxTexture.643*644* If either KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT or645* KTX_TEXTURE_CREATE_RAW_KVDATA_BIT is set then the ktxTexture's orientation646* fields will be set to defaults even if the KTX source contains647* KTXorientation metadata.648*649* @param[in] This pointer to a ktxTexture2-sized block of memory to650* initialize.651* @param[in] pStream pointer to the stream to read.652* @param[in] pHeader pointer to a KTX header that has already been read from653* the stream.654* @param[in] createFlags bitmask requesting specific actions during creation.655*656* @return KTX_SUCCESS on success, other KTX_* enum values on error.657*658* @exception KTX_FILE_DATA_ERROR659* Source data is inconsistent with the KTX660* specification.661* @exception KTX_FILE_READ_ERROR662* An error occurred while reading the source.663* @exception KTX_FILE_UNEXPECTED_EOF664* Not enough data in the source.665* @exception KTX_OUT_OF_MEMORY Not enough memory to load either the images or666* the key-value data.667* @exception KTX_UNKNOWN_FILE_FORMAT668* The source is not in KTX format.669* @exception KTX_UNSUPPORTED_TEXTURE_TYPE670* The source describes a texture type not671* supported by OpenGL or Vulkan, e.g, a 3D array.672*/673KTX_error_code674ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,675KTX_header2* pHeader,676ktxTextureCreateFlags createFlags)677{678ktxTexture2_private* private;679KTX_error_code result;680KTX_supplemental_info suppInfo;681ktxStream* stream;682struct BDFD* pBDFD;683ktx_size_t levelIndexSize;684685assert(pHeader != NULL && pStream != NULL);686687memset(This, 0, sizeof(*This));688result = ktxTexture_constructFromStream(ktxTexture(This), pStream,689createFlags);690if (result != KTX_SUCCESS)691return result;692693result = ktxCheckHeader2_(pHeader, &suppInfo);694if (result != KTX_SUCCESS)695goto cleanup;696// ktxCheckHeader2_ has done the max(1, levelCount) on pHeader->levelCount.697result = ktxTexture2_constructCommon(This, pHeader->levelCount);698if (result != KTX_SUCCESS)699goto cleanup;700private = This->_private;701702stream = ktxTexture2_getStream(This);703704/*705* Initialize from pHeader->info.706*/707This->vkFormat = pHeader->vkFormat;708This->supercompressionScheme = pHeader->supercompressionScheme;709710This->_protected->_typeSize = pHeader->typeSize;711// Can these be done by a ktxTexture_constructFromStream?712This->numDimensions = suppInfo.textureDimension;713This->baseWidth = pHeader->pixelWidth;714assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4);715switch (suppInfo.textureDimension) {716case 1:717This->baseHeight = This->baseDepth = 1;718break;719case 2:720This->baseHeight = pHeader->pixelHeight;721This->baseDepth = 1;722break;723case 3:724This->baseHeight = pHeader->pixelHeight;725This->baseDepth = pHeader->pixelDepth;726break;727}728if (pHeader->layerCount > 0) {729This->numLayers = pHeader->layerCount;730This->isArray = KTX_TRUE;731} else {732This->numLayers = 1;733This->isArray = KTX_FALSE;734}735This->numFaces = pHeader->faceCount;736if (pHeader->faceCount == 6)737This->isCubemap = KTX_TRUE;738else739This->isCubemap = KTX_FALSE;740// ktxCheckHeader2_ does the max(1, levelCount) and sets741// suppInfo.generateMipmaps when it was originally 0.742This->numLevels = pHeader->levelCount;743This->generateMipmaps = suppInfo.generateMipmaps;744745// Read level index746levelIndexSize = sizeof(ktxLevelIndexEntry) * This->numLevels;747result = stream->read(stream, &private->_levelIndex, levelIndexSize);748if (result != KTX_SUCCESS)749goto cleanup;750// Rebase index to start of data and save file offset.751private->_firstLevelFileOffset752= private->_levelIndex[This->numLevels-1].byteOffset;753for (ktx_uint32_t level = 0; level < This->numLevels; level++) {754private->_levelIndex[level].byteOffset755-= private->_firstLevelFileOffset;756if (This->supercompressionScheme == KTX_SS_NONE &&757private->_levelIndex[level].byteLength != private->_levelIndex[level].uncompressedByteLength) {758// For non-supercompressed files the levels must have matching byte lengths759result = KTX_FILE_DATA_ERROR;760}761}762if (result != KTX_SUCCESS)763goto cleanup;764765// Read DFD766if (pHeader->dataFormatDescriptor.byteOffset == 0 || pHeader->dataFormatDescriptor.byteLength < 16) {767// Missing or too small DFD768result = KTX_FILE_DATA_ERROR;769goto cleanup;770}771This->pDfd =772(ktx_uint32_t*)malloc(pHeader->dataFormatDescriptor.byteLength);773if (!This->pDfd) {774result = KTX_OUT_OF_MEMORY;775goto cleanup;776}777result = stream->read(stream, This->pDfd,778pHeader->dataFormatDescriptor.byteLength);779if (result != KTX_SUCCESS)780goto cleanup;781782if (pHeader->dataFormatDescriptor.byteLength != This->pDfd[0]) {783// DFD byteLength does not match dfdTotalSize784result = KTX_FILE_DATA_ERROR;785goto cleanup;786}787pBDFD = (struct BDFD*)(This->pDfd + 1);788if (pBDFD->descriptorBlockSize < 24 || (pBDFD->descriptorBlockSize - 24) % 16 != 0) {789// BDFD has invalid size790result = KTX_FILE_DATA_ERROR;791goto cleanup;792}793if (pBDFD->transfer > KHR_DF_TRANSFER_HLG_UNNORMALIZED_OETF) {794// Invalid transfer function795result = KTX_FILE_DATA_ERROR;796goto cleanup;797}798// No test for VK_FORMAT_UNDEFINED is needed here because:799// - any transfer function is allowed when vkFormat is UNDEFINED as with,800// e.g., some Basis Universal formats;801// - the following tests return false for VK_FORMAT_UNDEFINED.802if (isSrgbFormat(This->vkFormat) && pBDFD->transfer != KHR_DF_TRANSFER_SRGB) {803// Invalid transfer function804result = KTX_FILE_DATA_ERROR;805goto cleanup;806}807if (isNotSrgbFormatButHasSrgbVariant(This->vkFormat)808&& pBDFD->transfer == KHR_DF_TRANSFER_SRGB) {809// Invalid transfer function810result = KTX_FILE_DATA_ERROR;811goto cleanup;812}813814if (!ktxFormatSize_initFromDfd(&This->_protected->_formatSize, This->pDfd)) {815result = KTX_UNSUPPORTED_TEXTURE_TYPE;816goto cleanup;817}818This->isCompressed = (This->_protected->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);819820if (This->supercompressionScheme == KTX_SS_BASIS_LZ && pBDFD->model != KHR_DF_MODEL_ETC1S) {821result = KTX_FILE_DATA_ERROR;822goto cleanup;823}824825// Check compatibility with the KHR_texture_basisu glTF extension, if needed.826if (createFlags & KTX_TEXTURE_CREATE_CHECK_GLTF_BASISU_BIT) {827uint32_t max_dim = MAX(MAX(pHeader->pixelWidth, pHeader->pixelHeight), pHeader->pixelDepth);828uint32_t full_mip_pyramid_level_count = 1 + (uint32_t)log2(max_dim);829if (pHeader->levelCount != 1 && pHeader->levelCount != full_mip_pyramid_level_count) {830// KHR_texture_basisu requires full mip pyramid or single mip level831result = KTX_FILE_DATA_ERROR;832goto cleanup;833}834if (This->numDimensions != 2 || This->isArray || This->isCubemap) {835// KHR_texture_basisu requires 2D textures.836result = KTX_FILE_DATA_ERROR;837goto cleanup;838}839if ((This->baseWidth % 4) != 0 || (This->baseHeight % 4) != 0) {840// KHR_texture_basisu requires width and height to be a multiple of 4.841result = KTX_FILE_DATA_ERROR;842goto cleanup;843}844if (pBDFD->model != KHR_DF_MODEL_ETC1S && pBDFD->model != KHR_DF_MODEL_UASTC) {845// KHR_texture_basisu requires BasisLZ or UASTC846result = KTX_FILE_DATA_ERROR;847goto cleanup;848}849if (pBDFD->model == KHR_DF_MODEL_UASTC &&850This->supercompressionScheme != KTX_SS_NONE &&851This->supercompressionScheme != KTX_SS_ZSTD) {852// KHR_texture_basisu only allows NONE and ZSTD supercompression for UASTC853result = KTX_FILE_DATA_ERROR;854goto cleanup;855}856}857858uint32_t sampleCount = KHR_DFDSAMPLECOUNT(This->pDfd + 1);859if (sampleCount == 0) {860// Invalid sample count861result = KTX_FILE_DATA_ERROR;862goto cleanup;863}864if (pBDFD->model == KHR_DF_MODEL_ETC1S || pBDFD->model == KHR_DF_MODEL_UASTC) {865if (sampleCount < 1 || sampleCount > 2 || (sampleCount == 2 && pBDFD->model == KHR_DF_MODEL_UASTC)) {866// Invalid sample count867result = KTX_FILE_DATA_ERROR;868goto cleanup;869}870if (pBDFD->texelBlockDimension0 != 3 || pBDFD->texelBlockDimension1 != 3 ||871pBDFD->texelBlockDimension2 != 0 || pBDFD->texelBlockDimension3 != 0) {872// Texel block dimension must be 4x4x1x1 (offset by one)873result = KTX_FILE_DATA_ERROR;874goto cleanup;875}876}877878This->_private->_requiredLevelAlignment879= ktxTexture2_calcRequiredLevelAlignment(This);880881// Make an empty hash list.882ktxHashList_Construct(&This->kvDataHead);883// Load KVData.884if (pHeader->keyValueData.byteLength > 0) {885uint32_t expectedOffset = pHeader->dataFormatDescriptor.byteOffset + pHeader->dataFormatDescriptor.byteLength;886expectedOffset = (expectedOffset + 3) & ~0x3; // 4 byte aligned887if (pHeader->keyValueData.byteOffset != expectedOffset) {888result = KTX_FILE_DATA_ERROR;889goto cleanup;890}891if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) {892ktx_uint32_t kvdLen = pHeader->keyValueData.byteLength;893ktx_uint8_t* pKvd;894895pKvd = malloc(kvdLen);896if (pKvd == NULL) {897result = KTX_OUT_OF_MEMORY;898goto cleanup;899}900901result = stream->read(stream, pKvd, kvdLen);902if (result != KTX_SUCCESS) {903free(pKvd);904goto cleanup;905}906907if (IS_BIG_ENDIAN) {908/* Swap the counts inside the key & value data. */909ktx_uint8_t* src = pKvd;910ktx_uint8_t* end = pKvd + kvdLen;911while (src < end) {912ktx_uint32_t keyAndValueByteSize = *((ktx_uint32_t*)src);913_ktxSwapEndian32(&keyAndValueByteSize, 1);914src += _KTX_PAD4(keyAndValueByteSize);915}916}917918if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) {919char* orientationStr;920ktx_uint32_t orientationLen;921ktx_uint32_t animData[3];922ktx_uint32_t animDataLen;923924result = ktxHashList_Deserialize(&This->kvDataHead,925kvdLen, pKvd);926free(pKvd);927if (result != KTX_SUCCESS) {928goto cleanup;929}930931result = ktxHashList_FindValue(&This->kvDataHead,932KTX_ORIENTATION_KEY,933&orientationLen,934(void**)&orientationStr);935assert(result != KTX_INVALID_VALUE);936if (result == KTX_SUCCESS) {937// Length includes the terminating NUL.938if (orientationLen != This->numDimensions + 1) {939// There needs to be an entry for each dimension of940// the texture.941result = KTX_FILE_DATA_ERROR;942goto cleanup;943} else {944switch (This->numDimensions) {945case 3:946This->orientation.z = orientationStr[2];947FALLTHROUGH;948case 2:949This->orientation.y = orientationStr[1];950FALLTHROUGH;951case 1:952This->orientation.x = orientationStr[0];953}954}955} else {956result = KTX_SUCCESS; // Not finding orientation is okay.957}958result = ktxHashList_FindValue(&This->kvDataHead,959KTX_ANIMDATA_KEY,960&animDataLen,961(void**)animData);962assert(result != KTX_INVALID_VALUE);963if (result == KTX_SUCCESS) {964if (animDataLen != sizeof(animData)) {965result = KTX_FILE_DATA_ERROR;966goto cleanup;967}968if (This->isArray) {969This->isVideo = KTX_TRUE;970This->duration = animData[0];971This->timescale = animData[1];972This->loopcount = animData[2];973} else {974// animData is only valid for array textures.975result = KTX_FILE_DATA_ERROR;976goto cleanup;977}978} else {979result = KTX_SUCCESS; // Not finding video is okay.980}981} else {982This->kvDataLen = kvdLen;983This->kvData = pKvd;984}985} else {986stream->skip(stream, pHeader->keyValueData.byteLength);987}988} else if (pHeader->keyValueData.byteOffset != 0) {989// Non-zero KVD byteOffset with zero byteLength990result = KTX_FILE_DATA_ERROR;991goto cleanup;992}993994if (pHeader->supercompressionGlobalData.byteLength > 0) {995switch (This->supercompressionScheme) {996case KTX_SS_BASIS_LZ:997break;998case KTX_SS_NONE:999case KTX_SS_ZSTD:1000case KTX_SS_ZLIB:1001// In these cases SGD is not allowed1002result = KTX_FILE_DATA_ERROR;1003break;1004default:1005// We don't support other supercompression schemes1006result = KTX_UNSUPPORTED_FEATURE;1007break;1008}1009if (result != KTX_SUCCESS)1010goto cleanup;10111012// There could be padding here so seek to the next item.1013result = stream->setpos(stream,1014pHeader->supercompressionGlobalData.byteOffset);1015if (result != KTX_SUCCESS)1016goto cleanup;10171018// Read supercompressionGlobalData1019private->_supercompressionGlobalData =1020(ktx_uint8_t*)malloc(pHeader->supercompressionGlobalData.byteLength);1021if (!private->_supercompressionGlobalData) {1022result = KTX_OUT_OF_MEMORY;1023goto cleanup;1024}1025private->_sgdByteLength1026= pHeader->supercompressionGlobalData.byteLength;1027result = stream->read(stream, private->_supercompressionGlobalData,1028private->_sgdByteLength);10291030if (result != KTX_SUCCESS)1031goto cleanup;1032} else if (pHeader->supercompressionGlobalData.byteOffset != 0) {1033// Non-zero SGD byteOffset with zero byteLength1034result = KTX_FILE_DATA_ERROR;1035goto cleanup;1036} else if (This->supercompressionScheme == KTX_SS_BASIS_LZ) {1037// SGD is required for BasisLZ1038result = KTX_FILE_DATA_ERROR;1039goto cleanup;1040}10411042// Calculate size of the image data. Level 0 is the last level in the data.1043This->dataSize = private->_levelIndex[0].byteOffset1044+ private->_levelIndex[0].byteLength;10451046/*1047* Load the images, if requested.1048*/1049if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) {1050result = ktxTexture2_LoadImageData(This, NULL, 0);1051}1052if (result != KTX_SUCCESS)1053goto cleanup;10541055return result;10561057cleanup:1058ktxTexture2_destruct(This);1059return result;1060}10611062/**1063* @memberof ktxTexture2 @private1064* @~English1065* @brief Construct a ktxTexture from a ktxStream reading from a KTX source.1066*1067* The stream object is copied into the constructed ktxTexture2.1068*1069* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1070* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1071* will minimize memory usage by allowing, for example, loading the images1072* directly from the source into a Vulkan staging buffer.1073*1074* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1075* provided solely to enable implementation of the @e libktx v1 API on top of1076* ktxTexture.1077*1078* @param[in] This pointer to a ktxTexture2-sized block of memory to1079* initialize.1080* @param[in] pStream pointer to the stream to read.1081* @param[in] createFlags bitmask requesting specific actions during creation.1082*1083* @return KTX_SUCCESS on success, other KTX_* enum values on error.1084*1085* @exception KTX_FILE_READ_ERROR1086* An error occurred while reading the source.1087*1088* For other exceptions see ktxTexture2_constructFromStreamAndHeader().1089*/1090static KTX_error_code1091ktxTexture2_constructFromStream(ktxTexture2* This, ktxStream* pStream,1092ktxTextureCreateFlags createFlags)1093{1094KTX_header2 header;1095KTX_error_code result;10961097// Read header.1098result = pStream->read(pStream, &header, KTX2_HEADER_SIZE);1099if (result != KTX_SUCCESS)1100return result;11011102#if IS_BIG_ENDIAN1103// byte swap the header1104#endif1105return ktxTexture2_constructFromStreamAndHeader(This, pStream,1106&header, createFlags);1107}11081109/**1110* @memberof ktxTexture2 @private1111* @~English1112* @brief Construct a ktxTexture from a stdio stream reading from a KTX source.1113*1114* See ktxTextureInt_constructFromStream for details.1115*1116* @note Do not close the stdio stream until you are finished with the texture1117* object.1118*1119* @param[in] This pointer to a ktxTextureInt-sized block of memory to1120* initialize.1121* @param[in] stdioStream a stdio FILE pointer opened on the source.1122* @param[in] createFlags bitmask requesting specific actions during creation.1123*1124* @return KTX_SUCCESS on success, other KTX_* enum values on error.1125*1126* @exception KTX_INVALID_VALUE Either @p stdiostream or @p This is null.1127*1128* For other exceptions, see ktxTexture_constructFromStream().1129*/1130static KTX_error_code1131ktxTexture2_constructFromStdioStream(ktxTexture2* This, FILE* stdioStream,1132ktxTextureCreateFlags createFlags)1133{1134KTX_error_code result;1135ktxStream stream;11361137if (stdioStream == NULL || This == NULL)1138return KTX_INVALID_VALUE;11391140result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);1141if (result == KTX_SUCCESS)1142result = ktxTexture2_constructFromStream(This, &stream, createFlags);1143return result;1144}11451146/**1147* @memberof ktxTexture2 @private1148* @~English1149* @brief Construct a ktxTexture from a named KTX file.1150*1151* The file name must be encoded in utf-8. On Windows convert unicode names1152* to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.1153*1154* See ktxTextureInt_constructFromStream for details.1155*1156* @param[in] This pointer to a ktxTextureInt-sized block of memory to1157* initialize.1158* @param[in] filename pointer to a char array containing the file name.1159* @param[in] createFlags bitmask requesting specific actions during creation.1160*1161* @return KTX_SUCCESS on success, other KTX_* enum values on error.1162*1163* @exception KTX_FILE_OPEN_FAILED The file could not be opened.1164* @exception KTX_INVALID_VALUE @p filename is @c NULL.1165*1166* For other exceptions, see ktxTexture_constructFromStream().1167*/1168static KTX_error_code1169ktxTexture2_constructFromNamedFile(ktxTexture2* This,1170const char* const filename,1171ktxTextureCreateFlags createFlags)1172{1173KTX_error_code result;1174ktxStream stream;1175FILE* file;11761177if (This == NULL || filename == NULL)1178return KTX_INVALID_VALUE;11791180file = ktxFOpenUTF8(filename, "rb");1181if (!file)1182return KTX_FILE_OPEN_FAILED;11831184result = ktxFileStream_construct(&stream, file, KTX_TRUE);1185if (result == KTX_SUCCESS)1186result = ktxTexture2_constructFromStream(This, &stream, createFlags);11871188return result;1189}11901191/**1192* @memberof ktxTexture2 @private1193* @~English1194* @brief Construct a ktxTexture from KTX-formatted data in memory.1195*1196* See ktxTextureInt_constructFromStream for details.1197*1198* @param[in] This pointer to a ktxTextureInt-sized block of memory to1199* initialize.1200* @param[in] bytes pointer to the memory containing the serialized KTX data.1201* @param[in] size length of the KTX data in bytes.1202* @param[in] createFlags bitmask requesting specific actions during creation.1203*1204* @return KTX_SUCCESS on success, other KTX_* enum values on error.1205*1206* @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.1207*1208* For other exceptions, see ktxTexture_constructFromStream().1209*/1210static KTX_error_code1211ktxTexture2_constructFromMemory(ktxTexture2* This,1212const ktx_uint8_t* bytes, ktx_size_t size,1213ktxTextureCreateFlags createFlags)1214{1215KTX_error_code result;1216ktxStream stream;12171218if (bytes == NULL || size == 0)1219return KTX_INVALID_VALUE;12201221result = ktxMemStream_construct_ro(&stream, bytes, size);1222if (result == KTX_SUCCESS)1223result = ktxTexture2_constructFromStream(This, &stream, createFlags);12241225return result;1226}12271228/**1229* @memberof ktxTexture2 @private1230* @~English1231* @brief Destruct a ktxTexture2, freeing and internal memory.1232*1233* @param[in] This pointer to a ktxTexture2-sized block of memory to1234* initialize.1235*/1236void1237ktxTexture2_destruct(ktxTexture2* This)1238{1239if (This->pDfd) free(This->pDfd);1240if (This->_private) {1241ktx_uint8_t* sgd = This->_private->_supercompressionGlobalData;1242if (sgd) free(sgd);1243free(This->_private);1244}1245ktxTexture_destruct(ktxTexture(This));1246}12471248/*1249* In hindsight this function should have been `#if KTX_FEATURE_WRITE`.1250* In the interest of not breaking an app that may be using this in1251* `libktx_read` we won't change it.1252*/1253/**1254* @memberof ktxTexture21255* @ingroup writer1256* @~English1257* @brief Create a new empty ktxTexture2.1258*1259* The address of the newly created ktxTexture2 is written to the location1260* pointed at by @p newTex.1261*1262* @param[in] createInfo pointer to a ktxTextureCreateInfo struct with1263* information describing the texture.1264* @param[in] storageAllocation1265* enum indicating whether or not to allocate storage1266* for the texture images.1267* @param[in,out] newTex pointer to a location in which store the address of1268* the newly created texture.1269*1270* @return KTX_SUCCESS on success, other KTX_* enum values on error.1271*1272* @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a1273* valid OpenGL internal format value.1274* @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 21275* or 3.1276* @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in1277* @p createInfo is 0.1278* @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.1279* @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.1280* @exception KTX_INVALID_OPERATION1281* The <tt>base{Width,Height,Depth}</tt> specified1282* in @p createInfo are inconsistent with1283* @c numDimensions.1284* @exception KTX_INVALID_OPERATION1285* @p createInfo is requesting a 3D array or1286* 3D cubemap texture.1287* @exception KTX_INVALID_OPERATION1288* @p createInfo is requesting a cubemap with1289* non-square or non-2D images.1290* @exception KTX_INVALID_OPERATION1291* @p createInfo is requesting more mip levels1292* than needed for the specified1293* <tt>base{Width,Height,Depth}</tt>.1294* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture's images.1295*/1296KTX_error_code1297ktxTexture2_Create(const ktxTextureCreateInfo* const createInfo,1298ktxTextureCreateStorageEnum storageAllocation,1299ktxTexture2** newTex)1300{1301KTX_error_code result;13021303if (newTex == NULL)1304return KTX_INVALID_VALUE;13051306ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1307if (tex == NULL)1308return KTX_OUT_OF_MEMORY;13091310result = ktxTexture2_construct(tex, createInfo, storageAllocation);1311if (result != KTX_SUCCESS) {1312free(tex);1313} else {1314*newTex = tex;1315}1316return result;1317}13181319/**1320* @memberof ktxTexture21321* @ingroup writer1322* @~English1323* @brief Create a ktxTexture2 by making a copy of a ktxTexture2.1324*1325* The address of the newly created ktxTexture2 is written to the location1326* pointed at by @p newTex.1327*1328* @param[in] orig pointer to the texture to copy.1329* @param[in,out] newTex pointer to a location in which store the address of1330* the newly created texture.1331*1332* @return KTX_SUCCESS on success, other KTX_* enum values on error.1333*1334* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.1335*/1336KTX_error_code1337ktxTexture2_CreateCopy(ktxTexture2* orig, ktxTexture2** newTex)1338{1339KTX_error_code result;13401341if (newTex == NULL)1342return KTX_INVALID_VALUE;13431344ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1345if (tex == NULL)1346return KTX_OUT_OF_MEMORY;13471348result = ktxTexture2_constructCopy(tex, orig);1349if (result != KTX_SUCCESS) {1350free(tex);1351} else {1352*newTex = tex;1353}1354return result;13551356}13571358/**1359* @defgroup reader Reader1360* @brief Read KTX-formatted data.1361* @{1362*/13631364/**1365* @memberof ktxTexture21366* @~English1367* @brief Create a ktxTexture2 from a stdio stream reading from a KTX source.1368*1369* The address of a newly created ktxTexture2 reflecting the contents of the1370* stdio stream is written to the location pointed at by @p newTex.1371*1372* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1373* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1374* will minimize memory usage by allowing, for example, loading the images1375* directly from the source into a Vulkan staging buffer.1376*1377* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1378* provided solely to enable implementation of the @e libktx v1 API on top of1379* ktxTexture.1380*1381* @param[in] stdioStream stdio FILE pointer created from the desired file.1382* @param[in] createFlags bitmask requesting specific actions during creation.1383* @param[in,out] newTex pointer to a location in which store the address of1384* the newly created texture.1385*1386* @return KTX_SUCCESS on success, other KTX_* enum values on error.1387*1388* @exception KTX_INVALID_VALUE @p newTex is @c NULL.1389* @exception KTX_FILE_DATA_ERROR1390* Source data is inconsistent with the KTX1391* specification.1392* @exception KTX_FILE_READ_ERROR1393* An error occurred while reading the source.1394* @exception KTX_FILE_UNEXPECTED_EOF1395* Not enough data in the source.1396* @exception KTX_OUT_OF_MEMORY Not enough memory to create the texture object,1397* load the images or load the key-value data.1398* @exception KTX_UNKNOWN_FILE_FORMAT1399* The source is not in KTX format.1400* @exception KTX_UNSUPPORTED_TEXTURE_TYPE1401* The source describes a texture type not1402* supported by OpenGL or Vulkan, e.g, a 3D array.1403*/1404KTX_error_code1405ktxTexture2_CreateFromStdioStream(FILE* stdioStream,1406ktxTextureCreateFlags createFlags,1407ktxTexture2** newTex)1408{1409KTX_error_code result;1410if (newTex == NULL)1411return KTX_INVALID_VALUE;14121413ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1414if (tex == NULL)1415return KTX_OUT_OF_MEMORY;14161417result = ktxTexture2_constructFromStdioStream(tex, stdioStream,1418createFlags);1419if (result == KTX_SUCCESS)1420*newTex = (ktxTexture2*)tex;1421else {1422free(tex);1423*newTex = NULL;1424}1425return result;1426}14271428/**1429* @memberof ktxTexture21430* @~English1431* @brief Create a ktxTexture2 from a named KTX file.1432*1433* The address of a newly created ktxTexture2 reflecting the contents of the1434* file is written to the location pointed at by @p newTex.1435*1436* The file name must be encoded in utf-8. On Windows convert unicode names1437* to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.1438*1439* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1440* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1441* will minimize memory usage by allowing, for example, loading the images1442* directly from the source into a Vulkan staging buffer.1443*1444* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1445* provided solely to enable implementation of the @e libktx v1 API on top of1446* ktxTexture.1447*1448* @param[in] filename pointer to a char array containing the file name.1449* @param[in] createFlags bitmask requesting specific actions during creation.1450* @param[in,out] newTex pointer to a location in which store the address of1451* the newly created texture.1452*1453* @return KTX_SUCCESS on success, other KTX_* enum values on error.14541455* @exception KTX_FILE_OPEN_FAILED The file could not be opened.1456* @exception KTX_INVALID_VALUE @p filename is @c NULL.1457*1458* For other exceptions, see ktxTexture2_CreateFromStdioStream().1459*/1460KTX_error_code1461ktxTexture2_CreateFromNamedFile(const char* const filename,1462ktxTextureCreateFlags createFlags,1463ktxTexture2** newTex)1464{1465KTX_error_code result;14661467if (newTex == NULL)1468return KTX_INVALID_VALUE;14691470ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1471if (tex == NULL)1472return KTX_OUT_OF_MEMORY;14731474result = ktxTexture2_constructFromNamedFile(tex, filename, createFlags);1475if (result == KTX_SUCCESS)1476*newTex = (ktxTexture2*)tex;1477else {1478free(tex);1479*newTex = NULL;1480}1481return result;1482}14831484/**1485* @memberof ktxTexture21486* @~English1487* @brief Create a ktxTexture2 from KTX-formatted data in memory.1488*1489* The address of a newly created ktxTexture2 reflecting the contents of the1490* serialized KTX data is written to the location pointed at by @p newTex.1491*1492* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1493* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1494* will minimize memory usage by allowing, for example, loading the images1495* directly from the source into a Vulkan staging buffer.1496*1497* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1498* provided solely to enable implementation of the @e libktx v1 API on top of1499* ktxTexture.1500*1501* @param[in] bytes pointer to the memory containing the serialized KTX data.1502* @param[in] size length of the KTX data in bytes.1503* @param[in] createFlags bitmask requesting specific actions during creation.1504* @param[in,out] newTex pointer to a location in which store the address of1505* the newly created texture.1506*1507* @return KTX_SUCCESS on success, other KTX_* enum values on error.1508*1509* @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.1510*1511* For other exceptions, see ktxTexture2_CreateFromStdioStream().1512*/1513KTX_error_code1514ktxTexture2_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,1515ktxTextureCreateFlags createFlags,1516ktxTexture2** newTex)1517{1518KTX_error_code result;1519if (newTex == NULL)1520return KTX_INVALID_VALUE;15211522ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1523if (tex == NULL)1524return KTX_OUT_OF_MEMORY;15251526result = ktxTexture2_constructFromMemory(tex, bytes, size,1527createFlags);1528if (result == KTX_SUCCESS)1529*newTex = (ktxTexture2*)tex;1530else {1531free(tex);1532*newTex = NULL;1533}1534return result;1535}15361537/**1538* @memberof ktxTexture21539* @~English1540* @brief Create a ktxTexture2 from KTX-formatted data from a stream.1541*1542* The address of a newly created ktxTexture2 reflecting the contents of the1543* serialized KTX data is written to the location pointed at by @p newTex.1544*1545* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,1546* if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This1547* will minimize memory usage by allowing, for example, loading the images1548* directly from the source into a Vulkan staging buffer.1549*1550* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is1551* provided solely to enable implementation of the @e libktx v1 API on top of1552* ktxTexture.1553*1554* @param[in] stream pointer to the stream to read KTX data from.1555* @param[in] createFlags bitmask requesting specific actions during creation.1556* @param[in,out] newTex pointer to a location in which store the address of1557* the newly created texture.1558*1559* @return KTX_SUCCESS on success, other KTX_* enum values on error.1560*1561* @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.1562*1563* For other exceptions, see ktxTexture2_CreateFromStdioStream().1564*/1565KTX_error_code1566ktxTexture2_CreateFromStream(ktxStream* stream,1567ktxTextureCreateFlags createFlags,1568ktxTexture2** newTex)1569{1570KTX_error_code result;1571if (newTex == NULL)1572return KTX_INVALID_VALUE;15731574ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));1575if (tex == NULL)1576return KTX_OUT_OF_MEMORY;15771578result = ktxTexture2_constructFromStream(tex, stream, createFlags);1579if (result == KTX_SUCCESS)1580*newTex = (ktxTexture2*)tex;1581else {1582free(tex);1583*newTex = NULL;1584}1585return result;1586}15871588/**1589* @memberof ktxTexture21590* @~English1591* @brief Destroy a ktxTexture2 object.1592*1593* This frees the memory associated with the texture contents and the memory1594* of the ktxTexture2 object. This does @e not delete any OpenGL or Vulkan1595* texture objects created by ktxTexture2_GLUpload or ktxTexture2_VkUpload.1596*1597* @param[in] This pointer to the ktxTexture2 object to destroy1598*/1599void1600ktxTexture2_Destroy(ktxTexture2* This)1601{1602ktxTexture2_destruct(This);1603free(This);1604}16051606/**1607* @memberof ktxTexture2 @private1608* @~English1609* @brief Calculate the size of the image data for the specified number1610* of levels.1611*1612* The data size is the sum of the sizes of each level up to the number1613* specified and includes any @c mipPadding between levels. It does1614* not include initial @c mipPadding required in the file.1615*1616* @param[in] This pointer to the ktxTexture object of interest.1617* @param[in] levels number of levels whose data size to return.1618*1619* @return the data size in bytes.1620*/1621ktx_size_t1622ktxTexture2_calcDataSizeLevels(ktxTexture2* This, ktx_uint32_t levels)1623{1624ktx_size_t dataSize = 0;16251626assert(This != NULL);1627assert(This->supercompressionScheme == KTX_SS_NONE);1628assert(levels <= This->numLevels);1629for (ktx_uint32_t i = levels - 1; i > 0; i--) {1630ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,1631KTX_FORMAT_VERSION_TWO);1632dataSize += _KTX_PADN(This->_private->_requiredLevelAlignment,1633levelSize);1634}1635dataSize += ktxTexture_calcLevelSize(ktxTexture(This), 0,1636KTX_FORMAT_VERSION_TWO);1637return dataSize;1638}16391640/**1641* @memberof ktxTexture2 @private1642* @~English1643*1644* @copydoc ktxTexture::ktxTexture_doCalcFaceLodSize1645*/1646ktx_size_t1647ktxTexture2_calcFaceLodSize(ktxTexture2* This, ktx_uint32_t level)1648{1649assert(This != NULL);1650assert(This->supercompressionScheme == KTX_SS_NONE);1651/*1652* For non-array cubemaps this is the size of a face. For everything1653* else it is the size of the level.1654*/1655if (This->isCubemap && !This->isArray)1656return ktxTexture_calcImageSize(ktxTexture(This), level,1657KTX_FORMAT_VERSION_TWO);1658else1659return This->_private->_levelIndex[level].uncompressedByteLength;1660}16611662/**1663* @memberof ktxTexture2 @private1664* @~English1665* @brief Return the offset of a level in bytes from the start of the image1666* data in a ktxTexture.1667*1668* Since the offset is from the start of the image data, it does not include the initial1669* @c mipPadding required in the file.1670*1671* @param[in] This pointer to the ktxTexture object of interest.1672* @param[in] level level whose offset to return.1673*1674* @return the data size in bytes.1675*/1676ktx_size_t1677ktxTexture2_calcLevelOffset(ktxTexture2* This, ktx_uint32_t level)1678{1679assert (This != NULL);1680assert(This->supercompressionScheme == KTX_SS_NONE);1681assert (level < This->numLevels);1682ktx_size_t levelOffset = 0;1683for (ktx_uint32_t i = This->numLevels - 1; i > level; i--) {1684ktx_size_t levelSize;1685levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,1686KTX_FORMAT_VERSION_TWO);1687levelOffset += _KTX_PADN(This->_private->_requiredLevelAlignment,1688levelSize);1689}1690return levelOffset;1691}169216931694/**1695* @memberof ktxTexture2 @private1696* @~English1697* @brief Retrieve the offset of a level's first image within the KTX2 file.1698*1699* @param[in] This pointer to the ktxTexture object of interest.1700*/1701ktx_uint64_t ktxTexture2_levelFileOffset(ktxTexture2* This, ktx_uint32_t level)1702{1703assert(This->_private->_firstLevelFileOffset != 0);1704return This->_private->_levelIndex[level].byteOffset1705+ This->_private->_firstLevelFileOffset;1706}17071708// Recursive function to return the greatest common divisor of a and b.1709static uint32_t1710gcd(uint32_t a, uint32_t b) {1711if (a == 0)1712return b;1713return gcd(b % a, a);1714}17151716// Function to return the least common multiple of a & 4.1717uint32_t1718lcm4(uint32_t a)1719{1720if (!(a & 0x03))1721return a; // a is a multiple of 4.1722return (a*4) / gcd(a, 4);1723}17241725/**1726* @memberof ktxTexture2 @private1727* @~English1728* @brief Return the required alignment for levels of this texture.1729*1730* @param[in] This pointer to the ktxTexture2 object of interest.1731*1732* @return The required alignment for levels.1733*/1734ktx_uint32_t1735ktxTexture2_calcRequiredLevelAlignment(ktxTexture2* This)1736{1737ktx_uint32_t alignment;1738if (This->supercompressionScheme != KTX_SS_NONE)1739alignment = 1;1740else1741alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);1742return alignment;1743}17441745/**1746* @memberof ktxTexture2 @private1747* @~English1748* @brief Return what the required alignment for levels of this texture will be after inflation.1749*1750* @param[in] This pointer to the ktxTexture2 object of interest.1751*1752* @return The required alignment for levels.1753*/1754ktx_uint32_t1755ktxTexture2_calcPostInflationLevelAlignment(ktxTexture2* This)1756{1757ktx_uint32_t alignment;17581759// Should actually work for none supercompressed but don't want to1760// encourage use of it.1761assert(This->supercompressionScheme != KTX_SS_NONE && This->supercompressionScheme != KTX_SS_BASIS_LZ);17621763if (This->vkFormat != VK_FORMAT_UNDEFINED)1764alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);1765else1766alignment = 16;17671768return alignment;1769}177017711772/**1773* @memberof ktxTexture21774* @~English1775* @brief Return information about the components of an image in a texture.1776*1777* @param[in] This pointer to the ktxTexture object of interest.1778* @param[in,out] pNumComponents pointer to location in which to write the1779* number of components in the textures images.1780* @param[in,out] pComponentByteLength1781* pointer to the location in which to write1782* byte length of a component.1783*/1784void1785ktxTexture2_GetComponentInfo(ktxTexture2* This, uint32_t* pNumComponents,1786uint32_t* pComponentByteLength)1787{1788// FIXME Need to handle packed case.1789getDFDComponentInfoUnpacked(This->pDfd, pNumComponents,1790pComponentByteLength);1791}17921793/**1794* @memberof ktxTexture21795* @~English1796* @brief Return the number of components in an image of the texture.1797*1798* Returns the number of components indicated by the DFD's sample information1799* in accordance with the color model. For uncompressed formats it will be the actual1800* number of components in the image. For block-compressed formats, it will be 1 or 21801* according to the format's DFD color model. For Basis compressed textures, the1802* function examines the ids of the channels indicated by the DFD and uses that1803* information to determine and return the number of components in the image1804* @e before encoding and deflation so it can be used to help choose a suitable1805* transcode target format.1806*1807* @param[in] This pointer to the ktxTexture object of interest.1808*1809* @return the number of components.1810*/1811ktx_uint32_t1812ktxTexture2_GetNumComponents(ktxTexture2* This)1813{1814uint32_t* pBdb = This->pDfd + 1;1815uint32_t dfdNumComponents = getDFDNumComponents(This->pDfd);1816uint32_t colorModel = KHR_DFDVAL(pBdb, MODEL);1817if (colorModel < KHR_DF_MODEL_DXT1A) {1818return dfdNumComponents;1819} else {1820switch (colorModel) {1821case KHR_DF_MODEL_ETC1S:1822{1823uint32_t channel0Id = KHR_DFDSVAL(pBdb, 0, CHANNELID);1824if (dfdNumComponents == 1) {1825if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB)1826return 3;1827else1828return 1;1829} else {1830uint32_t channel1Id = KHR_DFDSVAL(pBdb, 1, CHANNELID);1831if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB1832&& channel1Id == KHR_DF_CHANNEL_ETC1S_AAA)1833return 4;1834else {1835// An invalid combination of channel Ids should never1836// have been set during creation or should have been1837// caught when the file was loaded.1838assert(channel0Id == KHR_DF_CHANNEL_ETC1S_RRR1839&& channel1Id == KHR_DF_CHANNEL_ETC1S_GGG);1840return 2;1841}1842}1843break;1844}1845case KHR_DF_MODEL_UASTC:1846switch (KHR_DFDSVAL(pBdb, 0, CHANNELID)) {1847case KHR_DF_CHANNEL_UASTC_RRR:1848return 1;1849case KHR_DF_CHANNEL_UASTC_RRRG:1850return 2;1851case KHR_DF_CHANNEL_UASTC_RGB:1852return 3;1853case KHR_DF_CHANNEL_UASTC_RGBA:1854return 4;1855default:1856// Same comment as for the assert in the ETC1 case.1857assert(false);1858return 1;1859}1860break;1861default:1862return dfdNumComponents;1863}1864}1865}18661867/**1868* @memberof ktxTexture21869* @~English1870* @brief Find the offset of an image within a ktxTexture's image data.1871*1872* As there is no such thing as a 3D cubemap we make the 3rd location parameter1873* do double duty. Only works for non-supercompressed textures as1874* there is no way to tell where an image is for a supercompressed one.1875*1876* @param[in] This pointer to the ktxTexture object of interest.1877* @param[in] level mip level of the image.1878* @param[in] layer array layer of the image.1879* @param[in] faceSlice cube map face or depth slice of the image.1880* @param[in,out] pOffset pointer to location to store the offset.1881*1882* @return KTX_SUCCESS on success, other KTX_* enum values on error.1883*1884* @exception KTX_INVALID_OPERATION1885* @p level, @p layer or @p faceSlice exceed the1886* dimensions of the texture.1887* @exception KTX_INVALID_OPERATION Texture is supercompressed.1888* @exception KTX_INVALID_VALID @p This is NULL.1889*/1890KTX_error_code1891ktxTexture2_GetImageOffset(ktxTexture2* This, ktx_uint32_t level,1892ktx_uint32_t layer, ktx_uint32_t faceSlice,1893ktx_size_t* pOffset)1894{1895if (This == NULL)1896return KTX_INVALID_VALUE;18971898if (level >= This->numLevels || layer >= This->numLayers)1899return KTX_INVALID_OPERATION;19001901if (This->supercompressionScheme != KTX_SS_NONE)1902return KTX_INVALID_OPERATION;19031904if (This->isCubemap) {1905if (faceSlice >= This->numFaces)1906return KTX_INVALID_OPERATION;1907} else {1908ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level);1909if (faceSlice >= maxSlice)1910return KTX_INVALID_OPERATION;1911}19121913// Get the offset of the start of the level.1914*pOffset = ktxTexture2_levelDataOffset(This, level);19151916// All layers, faces & slices within a level are the same size.1917if (layer != 0) {1918ktx_size_t layerSize;1919layerSize = ktxTexture_layerSize(ktxTexture(This), level,1920KTX_FORMAT_VERSION_TWO);1921*pOffset += layer * layerSize;1922}1923if (faceSlice != 0) {1924ktx_size_t imageSize;1925imageSize = ktxTexture2_GetImageSize(This, level);1926*pOffset += faceSlice * imageSize;1927}1928return KTX_SUCCESS;1929}19301931/**1932* @memberof ktxTexture21933* @~English1934* @brief Retrieve the transfer function of the images.1935*1936* @param[in] This pointer to the ktxTexture2 object of interest.1937*1938* @return A @c khr_df_transfer enum value specifying the transfer function.1939*/1940khr_df_transfer_e1941ktxTexture2_GetTransferFunction_e(ktxTexture2* This)1942{1943return KHR_DFDVAL(This->pDfd+1, TRANSFER);1944}19451946/**1947* @memberof ktxTexture21948* @~English1949* @brief Retrieve the transfer function of the images.1950* @deprecated Use ktxTexture2\_GetTransferFunction\_e. Now that the KTX1951* specification allows setting of non-linear transfer functions other than1952* sRGB, it is possible for the transfer function to be an EOTF so this1953* name is no longer appropriate.1954*1955* @param[in] This pointer to the ktxTexture2 object of interest.1956*1957* @return A @c khr_df_transfer enum value specifying the transfer function.1958*/1959khr_df_transfer_e1960ktxTexture2_GetOETF_e(ktxTexture2* This)1961{1962return KHR_DFDVAL(This->pDfd+1, TRANSFER);1963}19641965/**1966* @memberof ktxTexture21967* @~English1968* @brief Retrieve the transfer function of the images.1969* @deprecated Use ktxTexture2\_GetTransferFunction\_e.1970*1971* @param[in] This pointer to the ktxTexture2 object of interest.1972*1973* @return A @c khr_df_transfer enum value specifying the transfer function,1974* returned as @c ktx_uint32_t.1975*/1976ktx_uint32_t1977ktxTexture2_GetOETF(ktxTexture2* This)1978{1979return KHR_DFDVAL(This->pDfd+1, TRANSFER);1980}19811982/**1983* @memberof ktxTexture21984* @~English1985* @brief Retrieve the DFD color model of the images.1986*1987* @param[in] This pointer to the ktxTexture2 object of interest.1988*1989* @return A @c khr_df_transfer enum value specifying the color model.1990*/1991khr_df_model_e1992ktxTexture2_GetColorModel_e(ktxTexture2* This)1993{1994return KHR_DFDVAL(This->pDfd+1, MODEL);1995}19961997/**1998* @memberof ktxTexture21999* @~English2000* @brief Retrieve whether the RGB components have been premultiplied by the alpha component.2001*2002* @param[in] This pointer to the ktxTexture2 object of interest.2003*2004* @return KTX\_TRUE if the components are premultiplied, KTX_FALSE otherwise.2005*/2006ktx_bool_t2007ktxTexture2_GetPremultipliedAlpha(ktxTexture2* This)2008{2009return KHR_DFDVAL(This->pDfd+1, FLAGS) & KHR_DF_FLAG_ALPHA_PREMULTIPLIED;2010}20112012/**2013* @memberof ktxTexture22014* @~English2015* @brief Retrieve the color primaries of the images.2016*2017* @param[in] This pointer to the ktxTexture2 object of interest.2018*2019* @return A @c khr_df_primaries enum value specifying the primaries.2020*/2021khr_df_primaries_e2022ktxTexture2_GetPrimaries_e(ktxTexture2* This)2023{2024return KHR_DFDVAL(This->pDfd+1, PRIMARIES);2025}20262027/**2028* @memberof ktxTexture22029* @~English2030* @brief Query if the images are in a transcodable format.2031*2032* @param[in] This pointer to the ktxTexture2 object of interest.2033*/2034ktx_bool_t2035ktxTexture2_NeedsTranscoding(ktxTexture2* This)2036{2037if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_ETC1S)2038return true;2039else if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_UASTC)2040return true;2041else2042return false;2043}20442045#if KTX_FEATURE_WRITE2046/*2047* @memberof ktxTexture22048* @ingroup writer2049* @~English2050* @brief Set the transfer function for the images in a texture.2051*2052* @param[in] This pointer to the ktxTexture22053* @param[in] tf enumerator of the transfer function to set2054*2055* @return KTX_SUCCESS on success, other KTX_* enum values on error.2056*2057* @exception KTX_INVALID_OPERATION The transfer function is not valid for the2058* vkFormat of the texture.2059* @exception KTX_INVALID_VALUE The transfer function is not allowed by the2060* KTX spec.2061*/2062ktx_error_code_e2063ktxTexture2_SetTransferFunction(ktxTexture2* This, khr_df_transfer_e tf)2064{2065if (isSrgbFormat(This->vkFormat) && tf != KHR_DF_TRANSFER_SRGB)2066return KTX_INVALID_OPERATION;20672068if (isNotSrgbFormatButHasSrgbVariant(This->vkFormat) && tf == KHR_DF_TRANSFER_SRGB)2069return KTX_INVALID_OPERATION;20702071KHR_DFDSETVAL(This->pDfd + 1, TRANSFER, tf);2072return KTX_SUCCESS;2073}20742075/**2076* @memberof ktxTexture22077* @ingroup writer2078* @~English2079* @brief Set the transfer function for the images in a texture.2080* @deprecated Use ktxTexture2\_SetTransferFunction.2081*2082* @param[in] This pointer to the ktxTexture22083* @param[in] tf enumerator of the transfer function to set2084*/2085ktx_error_code_e2086ktxTexture2_SetOETF(ktxTexture2* This, khr_df_transfer_e tf)2087{2088return ktxTexture2_SetTransferFunction(This, tf);2089}20902091/**2092* @memberof ktxTexture22093* @ingroup writer2094* @~English2095* @brief Set the primaries for the images in a texture.2096*2097* @param[in] This pointer to the ktxTexture22098* @param[in] primaries enumerator of the primaries to set2099*/2100ktx_error_code_e2101ktxTexture2_SetPrimaries(ktxTexture2* This, khr_df_primaries_e primaries)2102{2103KHR_DFDSETVAL(This->pDfd + 1, PRIMARIES, primaries);2104return KTX_SUCCESS;2105}2106#endif21072108/**2109* @memberof ktxTexture22110* @~English2111* @brief Return the total size in bytes of the uncompressed data of a2112* ktxTexture2.2113*2114* If supercompressionScheme == @c KTX_SS_NONE or2115* @c KTX_SS_BASIS_LZ, returns the value of @c This->dataSize2116* else if supercompressionScheme == @c KTX_SS_ZSTD or @c KTX_SS_ZLIB, it2117* returns the sum of the uncompressed sizes of each mip level plus space for2118* the level padding. With no supercompression the data size and uncompressed2119* data size are the same. For Basis supercompression the uncompressed size2120* cannot be known until the data is transcoded so the compressed size is2121* returned.2122*2123* @param[in] This pointer to the ktxTexture1 object of interest.2124*/2125ktx_size_t2126ktxTexture2_GetDataSizeUncompressed(ktxTexture2* This)2127{2128switch (This->supercompressionScheme) {2129case KTX_SS_BASIS_LZ:2130case KTX_SS_NONE:2131return This->dataSize;2132case KTX_SS_ZSTD:2133case KTX_SS_ZLIB:2134{2135ktx_size_t uncompressedSize = 0;2136ktx_uint32_t uncompressedLevelAlignment;2137ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;21382139uncompressedLevelAlignment =2140ktxTexture2_calcPostInflationLevelAlignment(This);21412142for (ktx_int32_t level = This->numLevels - 1; level >= 1; level--) {2143ktx_size_t uncompressedLevelSize;2144uncompressedLevelSize = levelIndex[level].uncompressedByteLength;2145uncompressedLevelSize = _KTX_PADN(uncompressedLevelAlignment,2146uncompressedLevelSize);2147uncompressedSize += uncompressedLevelSize;2148}2149uncompressedSize += levelIndex[0].uncompressedByteLength;2150return uncompressedSize;2151}2152case KTX_SS_BEGIN_VENDOR_RANGE:2153case KTX_SS_END_VENDOR_RANGE:2154case KTX_SS_BEGIN_RESERVED:2155default:2156return 0;2157}2158}21592160/**2161* @memberof ktxTexture22162* @~English2163* @brief Calculate & return the size in bytes of an image at the specified2164* mip level.2165*2166* For arrays, this is the size of a layer, for cubemaps, the size of a face2167* and for 3D textures, the size of a depth slice.2168*2169* The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.2170*2171* @param[in] This pointer to the ktxTexture2 object of interest.2172* @param[in] level level of interest. *2173*/2174ktx_size_t2175ktxTexture2_GetImageSize(ktxTexture2* This, ktx_uint32_t level)2176{2177return ktxTexture_calcImageSize(ktxTexture(This), level,2178KTX_FORMAT_VERSION_TWO);2179}21802181/**2182* @memberof ktxTexture22183* @~English2184* @brief Calculate & return the size in bytes of all the images in the specified2185* mip level.2186*2187* For arrays, this is the size of all layers in the level, for cubemaps, the size of all2188* faces in the level and for 3D textures, the size of all depth slices in the level.2189*2190* @param[in] This pointer to the ktxTexture2 object of interest.2191* @param[in] level level of interest. *2192*/2193ktx_size_t2194ktxTexture2_GetLevelSize(ktxTexture2* This, ktx_uint32_t level)2195{2196return ktxTexture_calcLevelSize(ktxTexture(This), level,2197KTX_FORMAT_VERSION_TWO);2198}21992200/**2201* @memberof ktxTexture22202* @~English2203* @brief Iterate over the mip levels in a ktxTexture2 object.2204*2205* This is almost identical to ktxTexture_IterateLevelFaces(). The difference is2206* that the blocks of image data for non-array cube maps include all faces of2207* a mip level.2208*2209* This function works even if @p This->pData == 0 so it can be used to2210* obtain offsets and sizes for each level by callers who have loaded the data2211* externally.2212*2213* Intended for use only when supercompressionScheme == SUPERCOMPRESSION_NONE.2214*2215* @param[in] This handle of the ktxTexture opened on the data.2216* @param[in,out] iterCb the address of a callback function which is called2217* with the data for each image block.2218* @param[in,out] userdata the address of application-specific data which is2219* passed to the callback along with the image data.2220*2221* @return KTX_SUCCESS on success, other KTX_* enum values on error. The2222* following are returned directly by this function. @p iterCb may2223* return these for other causes or may return additional errors.2224*2225* @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not2226* decreasing2227* @exception KTX_INVALID_OPERATION supercompressionScheme != SUPERCOMPRESSION_NONE.2228* @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.2229*2230*/2231KTX_error_code2232ktxTexture2_IterateLevels(ktxTexture2* This, PFNKTXITERCB iterCb, void* userdata)2233{2234KTX_error_code result = KTX_SUCCESS;2235//ZSTD_DCtx* dctx;2236//ktx_uint8_t* decompBuf;2237ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;22382239if (This == NULL)2240return KTX_INVALID_VALUE;22412242if (iterCb == NULL)2243return KTX_INVALID_VALUE;22442245if (This->supercompressionScheme != KTX_SS_NONE)2246return KTX_INVALID_OPERATION;22472248for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)2249{2250ktx_uint32_t width, height, depth;2251ktx_uint64_t levelSize;2252ktx_uint64_t offset;22532254/* Array textures have the same number of layers at each mip level. */2255width = MAX(1, This->baseWidth >> level);2256height = MAX(1, This->baseHeight >> level);2257depth = MAX(1, This->baseDepth >> level);22582259levelSize = levelIndex[level].uncompressedByteLength;2260offset = ktxTexture2_levelDataOffset(This, level);22612262/* All array layers are passed in a group because that is how2263* GL & Vulkan need them. Hence no2264* for (layer = 0; layer < This->numLayers)2265*/2266result = iterCb(level, 0, width, height, depth,2267levelSize, This->pData + offset, userdata);2268if (result != KTX_SUCCESS)2269break;2270}22712272return result;2273}22742275/**2276* @memberof ktxTexture22277* @~English2278* @brief Iterate over the images in a ktxTexture2 object while loading the2279* image data.2280*2281* This operates similarly to ktxTexture_IterateLevelFaces() except that it2282* loads the images from the ktxTexture2's source to a temporary buffer2283* while iterating. If supercompressionScheme == KTX_SS_ZSTD or KTX_SS_ZLIB,2284* it will inflate the data before passing it to the callback. The callback function2285* must copy the image data if it wishes to preserve it as the temporary buffer2286* is reused for each level and is freed when this function exits.2287*2288* This function is helpful for reducing memory usage when uploading the data2289* to a graphics API.2290*2291* Intended for use only when supercompressionScheme == KTX_SS_NONE,2292* KTX_SS_ZSTD or KTX_SS_ZLIB. As there is no access to the ktxTexture's data on2293* conclusion of this function, destroying the texture on completion is recommended.2294*2295* @param[in] This pointer to the ktxTexture2 object of interest.2296* @param[in,out] iterCb the address of a callback function which is called2297* with the data for each image.2298* @param[in,out] userdata the address of application-specific data which is2299* passed to the callback along with the image data.2300*2301* @return KTX_SUCCESS on success, other KTX_* enum values on error. The2302* following are returned directly by this function. @p iterCb may2303* return these for other causes or may return additional errors.2304*2305* @exception KTX_FILE_DATA_ERROR mip level sizes are increasing not2306* decreasing2307* @exception KTX_INVALID_OPERATION the ktxTexture2 was not created from a2308* stream, i.e there is no data to load, or2309* this ktxTexture2's images have already2310* been loaded.2311* @exception KTX_INVALID_OPERATION2312* supercompressionScheme != KTX_SS_NONE,2313* supercompressionScheme != KTX_SS_ZSTD, and2314* supercompressionScheme != KTX_SS_ZLIB.2315* @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.2316* @exception KTX_OUT_OF_MEMORY not enough memory to allocate a block to2317* hold the base level image.2318*/2319KTX_error_code2320ktxTexture2_IterateLoadLevelFaces(ktxTexture2* This, PFNKTXITERCB iterCb,2321void* userdata)2322{2323DECLARE_PROTECTED(ktxTexture);2324ktxStream* stream = (ktxStream *)&prtctd->_stream;2325ktxLevelIndexEntry* levelIndex;2326ktx_size_t dataSize = 0, uncompressedDataSize = 0;2327KTX_error_code result = KTX_SUCCESS;2328ktx_uint8_t* dataBuf = NULL;2329ktx_uint8_t* uncompressedDataBuf = NULL;2330ktx_uint8_t* pData;2331ZSTD_DCtx* dctx = NULL;23322333if (This == NULL)2334return KTX_INVALID_VALUE;23352336if (This->classId != ktxTexture2_c)2337return KTX_INVALID_OPERATION;23382339if (This->supercompressionScheme != KTX_SS_NONE &&2340This->supercompressionScheme != KTX_SS_ZSTD &&2341This->supercompressionScheme != KTX_SS_ZLIB)2342return KTX_INVALID_OPERATION;23432344if (iterCb == NULL)2345return KTX_INVALID_VALUE;23462347if (prtctd->_stream.data.file == NULL)2348// This Texture not created from a stream or images are already loaded.2349return KTX_INVALID_OPERATION;23502351levelIndex = This->_private->_levelIndex;23522353// Allocate memory sufficient for the base level2354dataSize = levelIndex[0].byteLength;2355dataBuf = malloc(dataSize);2356if (!dataBuf)2357return KTX_OUT_OF_MEMORY;2358if (This->supercompressionScheme == KTX_SS_ZSTD || This->supercompressionScheme == KTX_SS_ZLIB) {2359uncompressedDataSize = levelIndex[0].uncompressedByteLength;2360uncompressedDataBuf = malloc(uncompressedDataSize);2361if (!uncompressedDataBuf) {2362result = KTX_OUT_OF_MEMORY;2363goto cleanup;2364}2365if (This->supercompressionScheme == KTX_SS_ZSTD) {2366dctx = ZSTD_createDCtx();2367}2368pData = uncompressedDataBuf;2369} else {2370pData = dataBuf;2371}23722373for (ktx_int32_t level = This->numLevels - 1; level >= 0; --level)2374{2375ktx_size_t levelSize;2376GLsizei width, height, depth;23772378// Array textures have the same number of layers at each mip level.2379width = MAX(1, This->baseWidth >> level);2380height = MAX(1, This->baseHeight >> level);2381depth = MAX(1, This->baseDepth >> level);23822383levelSize = levelIndex[level].byteLength;2384if (dataSize < levelSize) {2385// Levels cannot be larger than the base level2386result = KTX_FILE_DATA_ERROR;2387goto cleanup;2388}23892390// Use setpos so we skip any padding.2391result = stream->setpos(stream,2392ktxTexture2_levelFileOffset(This, level));2393if (result != KTX_SUCCESS)2394goto cleanup;23952396result = stream->read(stream, dataBuf, levelSize);2397if (result != KTX_SUCCESS)2398goto cleanup;23992400if (This->supercompressionScheme == KTX_SS_ZSTD) {2401levelSize =2402ZSTD_decompressDCtx(dctx, uncompressedDataBuf,2403uncompressedDataSize,2404dataBuf,2405levelSize);2406if (ZSTD_isError(levelSize)) {2407ZSTD_ErrorCode error = ZSTD_getErrorCode(levelSize);2408switch(error) {2409case ZSTD_error_dstSize_tooSmall:2410result = KTX_DECOMPRESS_LENGTH_ERROR; // inflatedDataCapacity too small.2411goto cleanup;2412case ZSTD_error_checksum_wrong:2413result = KTX_DECOMPRESS_CHECKSUM_ERROR;2414goto cleanup;2415case ZSTD_error_memory_allocation:2416result = KTX_OUT_OF_MEMORY;2417goto cleanup;2418default:2419result = KTX_FILE_DATA_ERROR;2420goto cleanup;2421}2422}24232424// We don't fix up the texture's dataSize, levelIndex or2425// _requiredAlignment because after this function completes there2426// is no way to get at the texture's data.2427//nindex[level].byteOffset = levelOffset;2428//nindex[level].uncompressedByteLength = nindex[level].byteLength =2429//levelByteLength;2430} else if (This->supercompressionScheme == KTX_SS_ZLIB) {2431result = ktxUncompressZLIBInt(uncompressedDataBuf,2432&uncompressedDataSize,2433dataBuf,2434levelSize);2435if (result != KTX_SUCCESS)2436return result;2437}24382439if (levelIndex[level].uncompressedByteLength != levelSize) {2440result = KTX_DECOMPRESS_LENGTH_ERROR;2441goto cleanup;2442}244324442445#if IS_BIG_ENDIAN2446switch (prtctd->_typeSize) {2447case 2:2448_ktxSwapEndian16((ktx_uint16_t*)pData, levelSize / 2);2449break;2450case 4:2451_ktxSwapEndian32((ktx_uint32_t*)pDest, levelSize / 4);2452break;2453case 8:2454_ktxSwapEndian64((ktx_uint64_t*)pDest, levelSize / 8);2455break;2456}2457#endif24582459// With the exception of non-array cubemaps the entire level2460// is passed at once because that is how OpenGL and Vulkan need them.2461// Vulkan could take all the faces at once too but we iterate2462// them separately for OpenGL.2463if (This->isCubemap && !This->isArray) {2464ktx_uint8_t* pFace = pData;2465struct blockCount {2466ktx_uint32_t x, y;2467} blockCount;2468ktx_size_t faceSize;24692470blockCount.x2471= (uint32_t)ceilf((float)width / prtctd->_formatSize.blockWidth);2472blockCount.y2473= (uint32_t)ceilf((float)height / prtctd->_formatSize.blockHeight);2474blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x);2475blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y);2476faceSize = blockCount.x * blockCount.y2477* prtctd->_formatSize.blockSizeInBits / 8;24782479for (ktx_uint32_t face = 0; face < This->numFaces; ++face) {2480result = iterCb(level, face,2481width, height, depth,2482(ktx_uint32_t)faceSize, pFace, userdata);2483pFace += faceSize;2484if (result != KTX_SUCCESS)2485goto cleanup;2486}2487} else {2488result = iterCb(level, 0,2489width, height, depth,2490(ktx_uint32_t)levelSize, pData, userdata);2491if (result != KTX_SUCCESS)2492goto cleanup;2493}2494}24952496// No further need for this.2497stream->destruct(stream);2498This->_private->_firstLevelFileOffset = 0;2499cleanup:2500free(dataBuf);2501if (uncompressedDataBuf) free(uncompressedDataBuf);2502if (dctx) ZSTD_freeDCtx(dctx);25032504return result;2505}25062507KTX_error_code2508ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,2509ktx_uint8_t* pInflatedData,2510ktx_size_t inflatedDataCapacity);25112512KTX_error_code2513ktxTexture2_inflateZLIBInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,2514ktx_uint8_t* pInflatedData,2515ktx_size_t inflatedDataCapacity);25162517typedef enum {2518LOADDATA_DONT_INFLATE_ON_LOAD,2519LOADDATA_INFLATE_ON_LOAD2520} ktxTexture2InflateFlagEnum;25212522/**2523* @memberof ktxTexture22524* @internal2525* @~English2526* @brief Load all the image data from the ktxTexture2's source.2527*2528* The data will be inflated if requested and supercompressionScheme == @c KTX_SS_ZSTD2529* or @c KTX_SS_ZLIB.2530* The data is loaded into the provided buffer or to an internally allocated2531* buffer, if @p pBuffer is @c NULL. Callers providing their own buffer must2532* ensure the buffer large enough to hold the inflated data for files deflated2533* with Zstd or ZLIB. See ktxTexture2\_GetDataSizeUncompressed().2534*2535* The texture's levelIndex, dataSize, DFD and supercompressionScheme will2536* all be updated after successful inflation to reflect the inflated data.2537*2538* @param[in] This pointer to the ktxTexture object of interest.2539* @param[in] pBuffer pointer to the buffer in which to load the image data.2540* @param[in] bufSize size of the buffer pointed at by @p pBuffer.2541* @param[in] inflateHandling enum indicating whether or not to inflate2542* supercompressed data.2543*2544* @return KTX_SUCCESS on success, other KTX_* enum values on error.2545*2546* @exception KTX_INVALID_VALUE @p This is NULL.2547* @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.2548* @exception KTX_INVALID_OPERATION2549* The data has already been loaded or the2550* ktxTexture was not created from a KTX source.2551* @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.2552*/2553ktx_error_code_e2554ktxTexture2_loadImageDataInt(ktxTexture2* This,2555ktx_uint8_t* pBuffer, ktx_size_t bufSize,2556ktxTexture2InflateFlagEnum inflateHandling)2557{2558DECLARE_PROTECTED(ktxTexture);2559DECLARE_PRIVATE(ktxTexture2);2560ktx_uint8_t* pDest;2561ktx_uint8_t* pDeflatedData = NULL;2562ktx_uint8_t* pReadBuf;2563KTX_error_code result = KTX_SUCCESS;2564ktx_size_t outputDataCapacity;2565ktx_bool_t doInflate = false;25662567if (This == NULL)2568return KTX_INVALID_VALUE;25692570if (This->pData != NULL)2571return KTX_INVALID_OPERATION; // Data already loaded.25722573if (prtctd->_stream.data.file == NULL)2574// This Texture not created from a stream or images already loaded;2575return KTX_INVALID_OPERATION;25762577if (inflateHandling == LOADDATA_INFLATE_ON_LOAD) {2578outputDataCapacity = ktxTexture2_GetDataSizeUncompressed(This);2579if (This->supercompressionScheme == KTX_SS_ZSTD || This->supercompressionScheme == KTX_SS_ZLIB)2580doInflate = true;2581} else {2582outputDataCapacity = This->dataSize;2583}25842585if (pBuffer == NULL) {2586This->pData = malloc(outputDataCapacity);2587if (This->pData == NULL)2588return KTX_OUT_OF_MEMORY;2589pDest = This->pData;2590} else if (bufSize < outputDataCapacity) {2591return KTX_INVALID_VALUE;2592} else {2593pDest = pBuffer;2594}25952596if (doInflate) {2597// Create buffer to hold deflated data.2598pDeflatedData = malloc(This->dataSize);2599if (pDeflatedData == NULL)2600return KTX_OUT_OF_MEMORY;2601pReadBuf = pDeflatedData;2602} else {2603pReadBuf = pDest;2604}26052606// Seek to data for first level as there may be padding between the2607// metadata/sgd and the image data.26082609result = prtctd->_stream.setpos(&prtctd->_stream,2610private->_firstLevelFileOffset);2611if (result != KTX_SUCCESS)2612goto cleanup;26132614result = prtctd->_stream.read(&prtctd->_stream, pReadBuf,2615This->dataSize);2616if (result != KTX_SUCCESS)2617goto cleanup;26182619if (doInflate) {2620assert(pDeflatedData != NULL);2621if (This->supercompressionScheme == KTX_SS_ZSTD) {2622result = ktxTexture2_inflateZstdInt(This, pDeflatedData, pDest,2623outputDataCapacity);2624} else if (This->supercompressionScheme == KTX_SS_ZLIB) {2625result = ktxTexture2_inflateZLIBInt(This, pDeflatedData, pDest,2626outputDataCapacity);2627}2628if (result != KTX_SUCCESS) {2629if (pBuffer == NULL) {2630free(This->pData);2631This->pData = 0;2632}2633goto cleanup;2634}2635}26362637if (IS_BIG_ENDIAN) {2638// Perform endianness conversion on texture data.2639// To avoid mip padding, need to convert each level individually.2640for (ktx_uint32_t level = 0; level < This->numLevels; ++level)2641{2642ktx_size_t levelOffset;2643ktx_size_t levelByteLength;26442645levelByteLength = private->_levelIndex[level].byteLength;2646levelOffset = ktxTexture2_levelDataOffset(This, level);2647pDest = This->pData + levelOffset;2648switch (prtctd->_typeSize) {2649case 2:2650_ktxSwapEndian16((ktx_uint16_t*)pDest, levelByteLength / 2);2651break;2652case 4:2653_ktxSwapEndian32((ktx_uint32_t*)pDest, levelByteLength / 4);2654break;2655case 8:2656_ktxSwapEndian64((ktx_uint64_t*)pDest, levelByteLength / 8);2657break;2658}2659}2660}26612662// No further need for stream or file offset.2663prtctd->_stream.destruct(&prtctd->_stream);2664private->_firstLevelFileOffset = 0;26652666cleanup:2667free(pDeflatedData);26682669return result;2670}26712672/**2673* @memberof ktxTexture22674* @~English2675* @brief Load all the image data from the ktxTexture2's source.2676*2677* The data will be inflated if supercompressionScheme == @c KTX_SS_ZSTD or2678* @c KTX_SS_ZLIB.2679* The data is loaded into the provided buffer or to an internally allocated2680* buffer, if @p pBuffer is @c NULL. Callers providing their own buffer must2681* ensure the buffer large enough to hold the inflated data for files deflated2682* with Zstd or ZLIB. See ktxTexture2\_GetDataSizeUncompressed().2683*2684* The texture's levelIndex, dataSize, DFD and supercompressionScheme will2685* all be updated after successful inflation to reflect the inflated data.2686*2687* @param[in] This pointer to the ktxTexture object of interest.2688* @param[in] pBuffer pointer to the buffer in which to load the image data.2689* @param[in] bufSize size of the buffer pointed at by @p pBuffer.2690*2691* @return KTX_SUCCESS on success, other KTX_* enum values on error.2692*2693* @exception KTX_INVALID_VALUE @p This is NULL.2694* @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.2695* @exception KTX_INVALID_OPERATION2696* The data has already been loaded or the2697* ktxTexture was not created from a KTX source.2698* @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.2699*/2700ktx_error_code_e2701ktxTexture2_LoadImageData(ktxTexture2* This,2702ktx_uint8_t* pBuffer, ktx_size_t bufSize)2703{2704return ktxTexture2_loadImageDataInt(This, pBuffer, bufSize, LOADDATA_INFLATE_ON_LOAD);2705}27062707/**2708* @memberof ktxTexture22709* @~English2710* @brief Load all the image data from the ktxTexture2's source without inflatiion..2711*2712* The data will be not be inflated if supercompressionScheme == @c KTX_SS_ZSTD or2713* @c KTX_SS_ZLIB. This function is provided to support some rare testing scenarios.2714* Generally use of ktxTexture2\_LoadImageData is highly recommended. For supercompressionScheme2715* values other than those mentioned, the result of this function is the same as2716* ktxTexture2\_LoadImageData.2717*2718* The data is loaded into the provided buffer or to an internally allocated2719* buffer, if @p pBuffer is @c NULL.2720*2721* @param[in] This pointer to the ktxTexture object of interest.2722* @param[in] pBuffer pointer to the buffer in which to load the image data.2723* @param[in] bufSize size of the buffer pointed at by @p pBuffer.2724*2725* @return KTX_SUCCESS on success, other KTX_* enum values on error.2726*2727* @exception KTX_INVALID_VALUE @p This is NULL.2728* @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.2729* @exception KTX_INVALID_OPERATION2730* The data has already been loaded or the2731* ktxTexture was not created from a KTX source.2732* @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.2733*/2734ktx_error_code_e2735ktxTexture2_LoadDeflatedImageData(ktxTexture2* This,2736ktx_uint8_t* pBuffer, ktx_size_t bufSize)2737{2738return ktxTexture2_loadImageDataInt(This, pBuffer, bufSize, LOADDATA_DONT_INFLATE_ON_LOAD);2739}27402741/**2742* @memberof ktxTexture2 @private2743* @~English2744* @brief Retrieve the offset of a level's first image within the ktxTexture2's2745* image data.2746*2747* @param[in] This pointer to the ktxTexture2 object of interest.2748*/2749ktx_uint64_t ktxTexture2_levelDataOffset(ktxTexture2* This, ktx_uint32_t level)2750{2751return This->_private->_levelIndex[level].byteOffset;2752}27532754/**2755* @memberof ktxTexture2 @private2756* @~English2757* @brief Inflate the data in a ktxTexture2 object using Zstandard.2758*2759* The texture's levelIndex, dataSize, DFD, data pointer, and supercompressionScheme will2760* all be updated after successful inflation to reflect the inflated data.2761*2762* @param[in] This pointer to the ktxTexture2 object of interest.2763* @param[in] pDeflatedData pointer to a buffer containing the deflated data2764* of the entire texture.2765* @param[in,out] pInflatedData pointer to a buffer in which to write the inflated2766* data.2767* @param[in] inflatedDataCapacity capacity of the buffer pointed at by2768* @p pInflatedData.2769*/2770KTX_error_code2771ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,2772ktx_uint8_t* pInflatedData,2773ktx_size_t inflatedDataCapacity)2774{2775ktx_uint32_t levelIndexByteLength =2776This->numLevels * sizeof(ktxLevelIndexEntry);2777uint64_t levelOffset = 0;2778ktxLevelIndexEntry* cindex = This->_private->_levelIndex;2779ktxLevelIndexEntry* nindex = NULL;2780ktx_uint32_t uncompressedLevelAlignment;2781ktx_error_code_e result = KTX_SUCCESS;27822783ZSTD_DCtx* dctx = NULL;27842785if (pDeflatedData == NULL)2786return KTX_INVALID_VALUE;27872788if (pInflatedData == NULL)2789return KTX_INVALID_VALUE;27902791if (This->supercompressionScheme != KTX_SS_ZSTD)2792return KTX_INVALID_OPERATION;27932794nindex = malloc(levelIndexByteLength);2795if (nindex == NULL)2796return KTX_OUT_OF_MEMORY;27972798uncompressedLevelAlignment =2799ktxTexture2_calcPostInflationLevelAlignment(This);28002801ktx_size_t inflatedByteLength = 0;2802dctx = ZSTD_createDCtx();2803if (dctx == NULL) {2804result = KTX_OUT_OF_MEMORY;2805goto cleanup;2806}2807for (int32_t level = This->numLevels - 1; level >= 0; level--) {2808size_t levelByteLength =2809ZSTD_decompressDCtx(dctx, pInflatedData + levelOffset,2810inflatedDataCapacity,2811&pDeflatedData[cindex[level].byteOffset],2812cindex[level].byteLength);2813if (ZSTD_isError(levelByteLength)) {2814ZSTD_ErrorCode error = ZSTD_getErrorCode(levelByteLength);2815switch(error) {2816case ZSTD_error_dstSize_tooSmall:2817result = KTX_DECOMPRESS_LENGTH_ERROR; // inflatedDataCapacity too small.2818goto cleanup;2819case ZSTD_error_checksum_wrong:2820result = KTX_DECOMPRESS_CHECKSUM_ERROR;2821goto cleanup;2822case ZSTD_error_memory_allocation:2823result = KTX_OUT_OF_MEMORY;2824goto cleanup;2825default:2826result = KTX_FILE_DATA_ERROR;2827goto cleanup;2828}2829}28302831if (This->_private->_levelIndex[level].uncompressedByteLength != levelByteLength) {2832result = KTX_DECOMPRESS_LENGTH_ERROR;2833goto cleanup;2834}28352836nindex[level].byteOffset = levelOffset;2837nindex[level].uncompressedByteLength = nindex[level].byteLength =2838levelByteLength;2839ktx_size_t paddedLevelByteLength2840= _KTX_PADN(uncompressedLevelAlignment, levelByteLength);2841inflatedByteLength += paddedLevelByteLength;2842levelOffset += paddedLevelByteLength;2843inflatedDataCapacity -= paddedLevelByteLength;2844}28452846// Now modify the texture.28472848This->dataSize = inflatedByteLength;2849This->supercompressionScheme = KTX_SS_NONE;2850memcpy(cindex, nindex, levelIndexByteLength); // Update level index2851This->_private->_requiredLevelAlignment = uncompressedLevelAlignment;28522853cleanup:2854ZSTD_freeDCtx(dctx);2855free(nindex);2856return result;2857}28582859/**2860* @memberof ktxTexture2 @private2861* @~English2862* @brief Inflate the data in a ktxTexture2 object using miniz (ZLIB).2863*2864* The texture's levelIndex, dataSize, DFD, data pointer, and supercompressionScheme will2865* all be updated after successful inflation to reflect the inflated data.2866*2867* @param[in] This pointer to the ktxTexture2 object of interest.2868* @param[in] pDeflatedData pointer to a buffer containing the deflated2869* data of the entire texture.2870* @param[in,out] pInflatedData pointer to a buffer in which to write the2871* inflated data.2872* @param[in] inflatedDataCapacity capacity of the buffer pointed at by2873* @p pInflatedData.2874*/2875KTX_error_code2876ktxTexture2_inflateZLIBInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,2877ktx_uint8_t* pInflatedData,2878ktx_size_t inflatedDataCapacity)2879{2880ktx_uint32_t levelIndexByteLength =2881This->numLevels * sizeof(ktxLevelIndexEntry);2882uint64_t levelOffset = 0;2883ktxLevelIndexEntry* cindex = This->_private->_levelIndex;2884ktxLevelIndexEntry* nindex;2885ktx_uint32_t uncompressedLevelAlignment;28862887if (pDeflatedData == NULL)2888return KTX_INVALID_VALUE;28892890if (pInflatedData == NULL)2891return KTX_INVALID_VALUE;28922893if (This->supercompressionScheme != KTX_SS_ZLIB)2894return KTX_INVALID_OPERATION;28952896nindex = malloc(levelIndexByteLength);2897if (nindex == NULL)2898return KTX_OUT_OF_MEMORY;28992900uncompressedLevelAlignment =2901ktxTexture2_calcPostInflationLevelAlignment(This);29022903ktx_size_t inflatedByteLength = 0;2904for (int32_t level = This->numLevels - 1; level >= 0; level--) {2905size_t levelByteLength = inflatedDataCapacity;2906KTX_error_code result = ktxUncompressZLIBInt(pInflatedData + levelOffset,2907&levelByteLength,2908&pDeflatedData[cindex[level].byteOffset],2909cindex[level].byteLength);2910if (result != KTX_SUCCESS) {2911free(nindex);2912return result;2913}29142915if (This->_private->_levelIndex[level].uncompressedByteLength != levelByteLength) {2916free(nindex);2917return KTX_DECOMPRESS_LENGTH_ERROR;2918}29192920nindex[level].byteOffset = levelOffset;2921nindex[level].uncompressedByteLength = nindex[level].byteLength =2922levelByteLength;2923ktx_size_t paddedLevelByteLength2924= _KTX_PADN(uncompressedLevelAlignment, levelByteLength);2925inflatedByteLength += paddedLevelByteLength;2926levelOffset += paddedLevelByteLength;2927inflatedDataCapacity -= paddedLevelByteLength;2928}29292930// Now modify the texture.29312932This->dataSize = inflatedByteLength;2933This->supercompressionScheme = KTX_SS_NONE;2934memcpy(cindex, nindex, levelIndexByteLength); // Update level index2935free(nindex);2936This->_private->_requiredLevelAlignment = uncompressedLevelAlignment;29372938return KTX_SUCCESS;2939}29402941#if !KTX_FEATURE_WRITE29422943/*2944* Stubs for writer functions that return a proper error code2945*/29462947KTX_error_code2948ktxTexture2_SetImageFromMemory(ktxTexture2* This, ktx_uint32_t level,2949ktx_uint32_t layer, ktx_uint32_t faceSlice,2950const ktx_uint8_t* src, ktx_size_t srcSize)2951{2952UNUSED(This);2953UNUSED(level);2954UNUSED(layer);2955UNUSED(faceSlice);2956UNUSED(src);2957UNUSED(srcSize);2958return KTX_INVALID_OPERATION;2959}29602961KTX_error_code2962ktxTexture2_SetImageFromStdioStream(ktxTexture2* This, ktx_uint32_t level,2963ktx_uint32_t layer, ktx_uint32_t faceSlice,2964FILE* src, ktx_size_t srcSize)2965{2966UNUSED(This);2967UNUSED(level);2968UNUSED(layer);2969UNUSED(faceSlice);2970UNUSED(src);2971UNUSED(srcSize);2972return KTX_INVALID_OPERATION;2973}29742975KTX_error_code2976ktxTexture2_WriteToStdioStream(ktxTexture2* This, FILE* dstsstr)2977{2978UNUSED(This);2979UNUSED(dstsstr);2980return KTX_INVALID_OPERATION;2981}29822983KTX_error_code2984ktxTexture2_WriteToNamedFile(ktxTexture2* This, const char* const dstname)2985{2986UNUSED(This);2987UNUSED(dstname);2988return KTX_INVALID_OPERATION;2989}29902991KTX_error_code2992ktxTexture2_WriteToMemory(ktxTexture2* This,2993ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)2994{2995UNUSED(This);2996UNUSED(ppDstBytes);2997UNUSED(pSize);2998return KTX_INVALID_OPERATION;2999}30003001KTX_error_code3002ktxTexture2_WriteToStream(ktxTexture2* This,3003ktxStream* dststr)3004{3005UNUSED(This);3006UNUSED(dststr);3007return KTX_INVALID_OPERATION;3008}30093010#endif30113012/*3013* Initialized here at the end to avoid the need for multiple declarations of3014* the virtual functions.3015*/30163017struct ktxTexture_vtblInt ktxTexture2_vtblInt = {3018(PFNCALCDATASIZELEVELS)ktxTexture2_calcDataSizeLevels,3019(PFNCALCFACELODSIZE)ktxTexture2_calcFaceLodSize,3020(PFNCALCLEVELOFFSET)ktxTexture2_calcLevelOffset3021};30223023struct ktxTexture_vtbl ktxTexture2_vtbl = {3024(PFNKTEXDESTROY)ktxTexture2_Destroy,3025(PFNKTEXGETIMAGEOFFSET)ktxTexture2_GetImageOffset,3026(PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture2_GetDataSizeUncompressed,3027(PFNKTEXGETIMAGESIZE)ktxTexture2_GetImageSize,3028(PFNKTEXGETLEVELSIZE)ktxTexture2_GetLevelSize,3029(PFNKTEXITERATELEVELS)ktxTexture2_IterateLevels,3030(PFNKTEXITERATELOADLEVELFACES)ktxTexture2_IterateLoadLevelFaces,3031(PFNKTEXNEEDSTRANSCODING)ktxTexture2_NeedsTranscoding,3032(PFNKTEXLOADIMAGEDATA)ktxTexture2_LoadImageData,3033(PFNKTEXSETIMAGEFROMMEMORY)ktxTexture2_SetImageFromMemory,3034(PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture2_SetImageFromStdioStream,3035(PFNKTEXWRITETOSTDIOSTREAM)ktxTexture2_WriteToStdioStream,3036(PFNKTEXWRITETONAMEDFILE)ktxTexture2_WriteToNamedFile,3037(PFNKTEXWRITETOMEMORY)ktxTexture2_WriteToMemory,3038(PFNKTEXWRITETOSTREAM)ktxTexture2_WriteToStream,3039};30403041/** @} */3042304330443045