/* -*- 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 ktxTexture1 implementation. Support for KTX format.14*15* @author Mark Callow, www.edgewise-consulting.com16*/1718#if defined(_WIN32)19#define _CRT_SECURE_NO_WARNINGS20#endif2122#include <stdlib.h>23#include <string.h>2425#include "dfdutils/dfd.h"26#include "ktx.h"27#include "ktxint.h"28#include "filestream.h"29#include "memstream.h"30#include "texture1.h"31#include "unused.h"32#include "gl_format.h"3334typedef struct ktxTexture1_private {35ktx_bool_t _needSwap;36} ktxTexture1_private;3738struct ktxTexture_vtbl ktxTexture1_vtbl;39struct ktxTexture_vtblInt ktxTexture1_vtblInt;4041static KTX_error_code42ktxTexture1_constructCommon(ktxTexture1* This)43{44assert(This != NULL);4546This->classId = ktxTexture1_c;47This->vtbl = &ktxTexture1_vtbl;48This->_protected->_vtbl = ktxTexture1_vtblInt;49This->_private = (ktxTexture1_private*)malloc(sizeof(ktxTexture1_private));50if (This->_private == NULL) {51return KTX_OUT_OF_MEMORY;52}53memset(This->_private, 0, sizeof(*This->_private));5455return KTX_SUCCESS;56}5758/**59* @memberof ktxTexture1 @private60* @copydoc ktxTexture2_construct61*/62static KTX_error_code63ktxTexture1_construct(ktxTexture1* This,64const ktxTextureCreateInfo* const createInfo,65ktxTextureCreateStorageEnum storageAllocation)66{67ktxTexture_protected* prtctd;68ktxFormatSize formatSize;69GLuint typeSize;70GLenum glFormat;71KTX_error_code result;7273memset(This, 0, sizeof(*This));7475This->glInternalformat = createInfo->glInternalformat;76glGetFormatSize(This->glInternalformat, &formatSize);77if (formatSize.blockSizeInBits == 0) {78// Most likely a deprecated legacy format.79return KTX_UNSUPPORTED_TEXTURE_TYPE;80}81glFormat= glGetFormatFromInternalFormat(createInfo->glInternalformat);82if (glFormat == GL_INVALID_VALUE) {83return KTX_INVALID_VALUE;84}85result = ktxTexture_construct(ktxTexture(This), createInfo, &formatSize);86if (result != KTX_SUCCESS)87return result;8889result = ktxTexture1_constructCommon(This);90if (result != KTX_SUCCESS)91return result;92prtctd = This->_protected;9394This->isCompressed95= (formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);96if (This->isCompressed) {97This->glFormat = 0;98This->glBaseInternalformat = glFormat;99This->glType = 0;100prtctd->_typeSize = 1;101} else {102This->glBaseInternalformat = This->glFormat = glFormat;103This->glType104= glGetTypeFromInternalFormat(createInfo->glInternalformat);105if (This->glType == GL_INVALID_VALUE) {106result = KTX_INVALID_VALUE;107goto cleanup;108}109typeSize = glGetTypeSizeFromType(This->glType);110assert(typeSize != GL_INVALID_VALUE);111112/* Do some sanity checking */113if (typeSize != 1 &&114typeSize != 2 &&115typeSize != 4)116{117/* Only 8, 16, and 32-bit types are supported for byte-swapping.118* See UNPACK_SWAP_BYTES & table 8.4 in the OpenGL 4.4 spec.119*/120result = KTX_INVALID_VALUE;121goto cleanup;122}123prtctd->_typeSize = typeSize;124}125126if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) {127This->dataSize128= ktxTexture_calcDataSizeTexture(ktxTexture(This));129This->pData = malloc(This->dataSize);130if (This->pData == NULL) {131result = KTX_OUT_OF_MEMORY;132goto cleanup;133}134}135return result;136137cleanup:138ktxTexture1_destruct(This);139ktxTexture_destruct(ktxTexture(This));140return result;141}142143/**144* @memberof ktxTexture1 @private145* @brief Construct a ktxTexture1 from a ktxStream reading from a KTX source.146*147* The KTX header, that must have been read prior to calling this, is passed148* to the function.149*150* The stream object is copied into the constructed ktxTexture1.151*152* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,153* if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This154* will minimize memory usage by allowing, for example, loading the images155* directly from the source into a Vulkan staging buffer.156*157* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is158* provided solely to enable implementation of the @e libktx v1 API on top of159* ktxTexture1.160*161* @param[in] This pointer to a ktxTexture1-sized block of memory to162* initialize.163* @param[in] pStream pointer to the stream to read.164* @param[in] pHeader pointer to a KTX header that has already been read from165* the stream.166* @param[in] createFlags bitmask requesting specific actions during creation.167*168* @return KTX_SUCCESS on success, other KTX_* enum values on error.169*170* @exception KTX_FILE_DATA_ERROR171* Source data is inconsistent with the KTX172* specification.173* @exception KTX_FILE_READ_ERROR174* An error occurred while reading the source.175* @exception KTX_FILE_UNEXPECTED_EOF176* Not enough data in the source.177* @exception KTX_OUT_OF_MEMORY Not enough memory to load either the images or178* the key-value data.179* @exception KTX_UNKNOWN_FILE_FORMAT180* The source is not in KTX format.181* @exception KTX_UNSUPPORTED_TEXTURE_TYPE182* The source describes a texture type not183* supported by OpenGL or Vulkan, e.g, a 3D array.184*/185KTX_error_code186ktxTexture1_constructFromStreamAndHeader(ktxTexture1* This, ktxStream* pStream,187KTX_header* pHeader,188ktxTextureCreateFlags createFlags)189{190ktxTexture1_private* private;191KTX_error_code result;192KTX_supplemental_info suppInfo;193ktxStream* stream;194ktx_off_t pos;195ktx_size_t size;196ktxFormatSize formatSize;197198assert(pHeader != NULL && pStream != NULL);199200memset(This, 0, sizeof(*This));201result = ktxTexture_constructFromStream(ktxTexture(This), pStream, createFlags);202if (result != KTX_SUCCESS)203return result;204result = ktxTexture1_constructCommon(This);205if (result != KTX_SUCCESS) {206ktxTexture_destruct(ktxTexture(This));207return result;208}209210private = This->_private;211stream = ktxTexture1_getStream(This);212213result = ktxCheckHeader1_(pHeader, &suppInfo);214if (result != KTX_SUCCESS)215goto cleanup;216217/*218* Initialize from pHeader info.219*/220This->glFormat = pHeader->glFormat;221This->glInternalformat = pHeader->glInternalformat;222This->glType = pHeader->glType;223glGetFormatSize(This->glInternalformat, &formatSize);224if (formatSize.blockSizeInBits == 0) {225// Most likely a deprecated legacy format.226result = KTX_UNSUPPORTED_TEXTURE_TYPE;227goto cleanup;228}229This->_protected->_formatSize = formatSize;230This->glBaseInternalformat = pHeader->glBaseInternalformat;231// Can these be done by a ktxTexture_constructFromStream?232This->numDimensions = suppInfo.textureDimension;233This->baseWidth = pHeader->pixelWidth;234assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4);235switch (suppInfo.textureDimension) {236case 1:237This->baseHeight = This->baseDepth = 1;238break;239case 2:240This->baseHeight = pHeader->pixelHeight;241This->baseDepth = 1;242break;243case 3:244This->baseHeight = pHeader->pixelHeight;245This->baseDepth = pHeader->pixelDepth;246break;247}248if (pHeader->numberOfArrayElements > 0) {249This->numLayers = pHeader->numberOfArrayElements;250This->isArray = KTX_TRUE;251} else {252This->numLayers = 1;253This->isArray = KTX_FALSE;254}255This->numFaces = pHeader->numberOfFaces;256if (pHeader->numberOfFaces == 6)257This->isCubemap = KTX_TRUE;258else259This->isCubemap = KTX_FALSE;260This->numLevels = pHeader->numberOfMipLevels;261This->isCompressed = suppInfo.compressed;262This->generateMipmaps = suppInfo.generateMipmaps;263if (pHeader->endianness == KTX_ENDIAN_REF_REV)264private->_needSwap = KTX_TRUE;265This->_protected->_typeSize = pHeader->glTypeSize;266267/*268* Make an empty hash list.269*/270ktxHashList_Construct(&This->kvDataHead);271/*272* Load KVData.273*/274if (pHeader->bytesOfKeyValueData > 0) {275if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) {276ktx_uint32_t kvdLen = pHeader->bytesOfKeyValueData;277ktx_uint8_t* pKvd;278279pKvd = malloc(kvdLen);280if (pKvd == NULL) {281result = KTX_OUT_OF_MEMORY;282goto cleanup;283}284285result = stream->read(stream, pKvd, kvdLen);286if (result != KTX_SUCCESS)287goto cleanup;288289if (private->_needSwap) {290/* Swap the counts inside the key & value data. */291ktx_uint8_t* src = pKvd;292ktx_uint8_t* end = pKvd + kvdLen;293while (src < end) {294ktx_uint32_t* pKeyAndValueByteSize = (ktx_uint32_t*)src;295_ktxSwapEndian32(pKeyAndValueByteSize, 1);296src += _KTX_PAD4(*pKeyAndValueByteSize);297}298}299300if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) {301char* orientation;302ktx_uint32_t orientationLen;303304result = ktxHashList_Deserialize(&This->kvDataHead,305kvdLen, pKvd);306free(pKvd);307if (result != KTX_SUCCESS) {308goto cleanup;309}310311result = ktxHashList_FindValue(&This->kvDataHead,312KTX_ORIENTATION_KEY,313&orientationLen,314(void**)&orientation);315assert(result != KTX_INVALID_VALUE);316if (result == KTX_SUCCESS) {317ktx_uint32_t count;318char orient[4] = {0, 0, 0, 0};319320count = sscanf(orientation, KTX_ORIENTATION3_FMT,321&orient[0],322&orient[1],323&orient[2]);324325if (count > This->numDimensions) {326// KTX 1 is less strict than KTX2 so there is a chance327// of having more dimensions than needed.328count = This->numDimensions;329}330switch (This->numDimensions) {331case 3:332This->orientation.z = orient[2];333FALLTHROUGH;334case 2:335This->orientation.y = orient[1];336FALLTHROUGH;337case 1:338This->orientation.x = orient[0];339}340}341} else {342This->kvDataLen = kvdLen;343This->kvData = pKvd;344}345} else {346stream->skip(stream, pHeader->bytesOfKeyValueData);347}348}349350/*351* Get the size of the image data.352*/353result = stream->getsize(stream, &size);354if (result != KTX_SUCCESS)355goto cleanup;356357result = stream->getpos(stream, &pos);358if (result != KTX_SUCCESS)359goto cleanup;360361/* Remove space for faceLodSize fields */362This->dataSize = size - pos - This->numLevels * sizeof(ktx_uint32_t);363364/*365* Load the images, if requested.366*/367if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) {368result = ktxTexture1_LoadImageData(This, NULL, 0);369}370if (result == KTX_SUCCESS)371return result;372373cleanup:374ktxTexture1_destruct(This);375return result;376}377378/**379* @memberof ktxTexture1 @private380* @brief Construct a ktxTexture1 from a ktxStream reading from a KTX source.381*382* The stream object is copied into the constructed ktxTexture1.383*384* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,385* if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This386* will minimize memory usage by allowing, for example, loading the images387* directly from the source into a Vulkan staging buffer.388*389* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is390* provided solely to enable implementation of the @e libktx v1 API on top of391* ktxTexture1.392*393* @param[in] This pointer to a ktxTexture1-sized block of memory to394* initialize.395* @param[in] pStream pointer to the stream to read.396* @param[in] createFlags bitmask requesting specific actions during creation.397*398* @return KTX_SUCCESS on success, other KTX_* enum values on error.399*400* @exception KTX_FILE_READ_ERROR401* An error occurred while reading the source.402*403* For other exceptions see ktxTexture1_constructFromStreamAndHeader().404*/405static KTX_error_code406ktxTexture1_constructFromStream(ktxTexture1* This, ktxStream* pStream,407ktxTextureCreateFlags createFlags)408{409KTX_header header;410KTX_error_code result;411412// Read header.413result = pStream->read(pStream, &header, KTX_HEADER_SIZE);414if (result != KTX_SUCCESS)415return result;416417return ktxTexture1_constructFromStreamAndHeader(This, pStream,418&header, createFlags);419}420421/**422* @memberof ktxTexture1 @private423* @brief Construct a ktxTexture1 from a stdio stream reading from a KTX source.424*425* See ktxTextureInt_constructFromStream for details.426*427* @note Do not close the stdio stream until you are finished with the texture428* object.429*430* @param[in] This pointer to a ktxTextureInt-sized block of memory to431* initialize.432* @param[in] stdioStream a stdio FILE pointer opened on the source.433* @param[in] createFlags bitmask requesting specific actions during creation.434*435* @return KTX_SUCCESS on success, other KTX_* enum values on error.436*437* @exception KTX_INVALID_VALUE Either @p stdiostream or @p This is null.438*439* For other exceptions, see ktxTexture_constructFromStream().440*/441static KTX_error_code442ktxTexture1_constructFromStdioStream(ktxTexture1* This, FILE* stdioStream,443ktxTextureCreateFlags createFlags)444{445ktxStream stream;446KTX_error_code result;447448if (stdioStream == NULL || This == NULL)449return KTX_INVALID_VALUE;450451result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);452if (result == KTX_SUCCESS)453result = ktxTexture1_constructFromStream(This, &stream, createFlags);454return result;455}456457/**458* @memberof ktxTexture1 @private459* @brief Construct a ktxTexture1 from a named KTX file.460*461* The file name must be encoded in utf-8. On Windows convert unicode names462* to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.463*464* See ktxTextureInt_constructFromStream for details.465*466* @param[in] This pointer to a ktxTextureInt-sized block of memory to467* initialize.468* @param[in] filename pointer to a char array containing the file name.469* @param[in] createFlags bitmask requesting specific actions during creation.470*471* @return KTX_SUCCESS on success, other KTX_* enum values on error.472*473* @exception KTX_FILE_OPEN_FAILED The file could not be opened.474* @exception KTX_INVALID_VALUE @p filename is @c NULL.475*476* For other exceptions, see ktxTexture_constructFromStream().477*/478static KTX_error_code479ktxTexture1_constructFromNamedFile(ktxTexture1* This,480const char* const filename,481ktxTextureCreateFlags createFlags)482{483FILE* file;484ktxStream stream;485KTX_error_code result;486487if (This == NULL || filename == NULL)488return KTX_INVALID_VALUE;489490file = ktxFOpenUTF8(filename, "rb");491if (!file)492return KTX_FILE_OPEN_FAILED;493494result = ktxFileStream_construct(&stream, file, KTX_TRUE);495if (result == KTX_SUCCESS)496result = ktxTexture1_constructFromStream(This, &stream, createFlags);497498return result;499}500501/**502* @memberof ktxTexture1 @private503* @brief Construct a ktxTexture1 from KTX-formatted data in memory.504*505* See ktxTextureInt_constructFromStream for details.506*507* @param[in] This pointer to a ktxTextureInt-sized block of memory to508* initialize.509* @param[in] bytes pointer to the memory containing the serialized KTX data.510* @param[in] size length of the KTX data in bytes.511* @param[in] createFlags bitmask requesting specific actions during creation.512*513* @return KTX_SUCCESS on success, other KTX_* enum values on error.514*515* @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.516*517* For other exceptions, see ktxTexture_constructFromStream().518*/519static KTX_error_code520ktxTexture1_constructFromMemory(ktxTexture1* This,521const ktx_uint8_t* bytes, ktx_size_t size,522ktxTextureCreateFlags createFlags)523{524ktxStream stream;525KTX_error_code result;526527if (bytes == NULL || size == 0)528return KTX_INVALID_VALUE;529530result = ktxMemStream_construct_ro(&stream, bytes, size);531if (result == KTX_SUCCESS)532result = ktxTexture1_constructFromStream(This, &stream, createFlags);533534return result;535}536537void538ktxTexture1_destruct(ktxTexture1* This)539{540if (This->_private) free(This->_private);541ktxTexture_destruct(ktxTexture(This));542}543544/**545* @defgroup reader Reader546* @brief Read KTX-formatted data.547* @{548*/549550/**551* @memberof ktxTexture1552* @ingroup writer553* @brief Create a new empty ktxTexture1.554*555* The address of the newly created ktxTexture1 is written to the location556* pointed at by @p newTex.557*558* @param[in] createInfo pointer to a ktxTextureCreateInfo struct with559* information describing the texture.560* @param[in] storageAllocation561* enum indicating whether or not to allocate storage562* for the texture images.563* @param[in,out] newTex pointer to a location in which store the address of564* the newly created texture.565*566* @return KTX_SUCCESS on success, other KTX_* enum values on error.567*568* @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a569* valid OpenGL internal format value.570* @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2571* or 3.572* @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in573* @p createInfo is 0.574* @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.575* @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.576* @exception KTX_INVALID_OPERATION577* The <tt>base{Width,Height,Depth}</tt> specified578* in @p createInfo are inconsistent with579* @c numDimensions.580* @exception KTX_INVALID_OPERATION581* @p createInfo is requesting a 3D array or582* 3D cubemap texture.583* @exception KTX_INVALID_OPERATION584* @p createInfo is requesting a cubemap with585* non-square or non-2D images.586* @exception KTX_INVALID_OPERATION587* @p createInfo is requesting more mip levels588* than needed for the specified589* <tt>base{Width,Height,Depth}</tt>.590* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture's images.591*/592KTX_error_code593ktxTexture1_Create(const ktxTextureCreateInfo* const createInfo,594ktxTextureCreateStorageEnum storageAllocation,595ktxTexture1** newTex)596{597KTX_error_code result;598599if (newTex == NULL)600return KTX_INVALID_VALUE;601602ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));603if (tex == NULL)604return KTX_OUT_OF_MEMORY;605606result = ktxTexture1_construct(tex, createInfo, storageAllocation);607if (result != KTX_SUCCESS) {608free(tex);609} else {610*newTex = tex;611}612return result;613}614615/**616* @memberof ktxTexture1617* @~English618* @brief Create a ktxTexture1 from a stdio stream reading from a KTX source.619*620* The address of a newly created texture reflecting the contents of the621* stdio stream is written to the location pointed at by @p newTex.622*623* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,624* if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This625* will minimize memory usage by allowing, for example, loading the images626* directly from the source into a Vulkan staging buffer.627*628* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is629* provided solely to enable implementation of the @e libktx v1 API on top of630* ktxTexture1.631*632* @param[in] stdioStream stdio FILE pointer created from the desired file.633* @param[in] createFlags bitmask requesting specific actions during creation.634* @param[in,out] newTex pointer to a location in which store the address of635* the newly created texture.636*637* @return KTX_SUCCESS on success, other KTX_* enum values on error.638*639* @exception KTX_INVALID_VALUE @p newTex is @c NULL.640* @exception KTX_FILE_DATA_ERROR641* Source data is inconsistent with the KTX642* specification.643* @exception KTX_FILE_READ_ERROR644* An error occurred while reading the source.645* @exception KTX_FILE_UNEXPECTED_EOF646* Not enough data in the source.647* @exception KTX_OUT_OF_MEMORY Not enough memory to create the texture object,648* load the images or load the key-value data.649* @exception KTX_UNKNOWN_FILE_FORMAT650* The source is not in KTX format.651* @exception KTX_UNSUPPORTED_TEXTURE_TYPE652* The source describes a texture type not653* supported by OpenGL or Vulkan, e.g, a 3D array.654*/655KTX_error_code656ktxTexture1_CreateFromStdioStream(FILE* stdioStream,657ktxTextureCreateFlags createFlags,658ktxTexture1** newTex)659{660KTX_error_code result;661if (newTex == NULL)662return KTX_INVALID_VALUE;663664ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));665if (tex == NULL)666return KTX_OUT_OF_MEMORY;667668result = ktxTexture1_constructFromStdioStream(tex, stdioStream,669createFlags);670if (result == KTX_SUCCESS)671*newTex = (ktxTexture1*)tex;672else {673free(tex);674*newTex = NULL;675}676return result;677}678679/**680* @memberof ktxTexture1681* @~English682* @brief Create a ktxTexture1 from a named KTX file.683*684* The address of a newly created texture reflecting the contents of the685* file is written to the location pointed at by @p newTex.686*687* The file name must be encoded in utf-8. On Windows convert unicode names688* to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.689*690* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,691* if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This692* will minimize memory usage by allowing, for example, loading the images693* directly from the source into a Vulkan staging buffer.694*695* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is696* provided solely to enable implementation of the @e libktx v1 API on top of697* ktxTexture1.698*699* @param[in] filename pointer to a char array containing the file name.700* @param[in] createFlags bitmask requesting specific actions during creation.701* @param[in,out] newTex pointer to a location in which store the address of702* the newly created texture.703*704* @return KTX_SUCCESS on success, other KTX_* enum values on error.705*706* @exception KTX_FILE_OPEN_FAILED The file could not be opened.707* @exception KTX_INVALID_VALUE @p filename is @c NULL.708*709* For other exceptions, see ktxTexture1_CreateFromStdioStream().710*/711KTX_error_code712ktxTexture1_CreateFromNamedFile(const char* const filename,713ktxTextureCreateFlags createFlags,714ktxTexture1** newTex)715{716KTX_error_code result;717718if (newTex == NULL)719return KTX_INVALID_VALUE;720721ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));722if (tex == NULL)723return KTX_OUT_OF_MEMORY;724725result = ktxTexture1_constructFromNamedFile(tex, filename, createFlags);726if (result == KTX_SUCCESS)727*newTex = (ktxTexture1*)tex;728else {729free(tex);730*newTex = NULL;731}732return result;733}734735/**736* @memberof ktxTexture1737* @~English738* @brief Create a ktxTexture1 from KTX-formatted data in memory.739*740* The address of a newly created texture reflecting the contents of the741* serialized KTX data is written to the location pointed at by @p newTex.742*743* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,744* if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This745* will minimize memory usage by allowing, for example, loading the images746* directly from the source into a Vulkan staging buffer.747*748* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is749* provided solely to enable implementation of the @e libktx v1 API on top of750* ktxTexture1.751*752* @param[in] bytes pointer to the memory containing the serialized KTX data.753* @param[in] size length of the KTX data in bytes.754* @param[in] createFlags bitmask requesting specific actions during creation.755* @param[in,out] newTex pointer to a location in which store the address of756* the newly created texture.757*758* @return KTX_SUCCESS on success, other KTX_* enum values on error.759*760* @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.761*762* For other exceptions, see ktxTexture1_CreateFromStdioStream().763*/764KTX_error_code765ktxTexture1_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,766ktxTextureCreateFlags createFlags,767ktxTexture1** newTex)768{769KTX_error_code result;770if (newTex == NULL)771return KTX_INVALID_VALUE;772773ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));774if (tex == NULL)775return KTX_OUT_OF_MEMORY;776777result = ktxTexture1_constructFromMemory(tex, bytes, size,778createFlags);779if (result == KTX_SUCCESS)780*newTex = (ktxTexture1*)tex;781else {782free(tex);783*newTex = NULL;784}785return result;786}787788/**789* @memberof ktxTexture1790* @~English791* @brief Create a ktxTexture1 from KTX-formatted data from a `ktxStream`.792*793* The address of a newly created texture reflecting the contents of the794* serialized KTX data is written to the location pointed at by @p newTex.795*796* The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,797* if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This798* will minimize memory usage by allowing, for example, loading the images799* directly from the source into a Vulkan staging buffer.800*801* The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is802* provided solely to enable implementation of the @e libktx v1 API on top of803* ktxTexture1.804*805* @param[in] pStream pointer to the stream to read KTX data from.806* @param[in] createFlags bitmask requesting specific actions during creation.807* @param[in,out] newTex pointer to a location in which store the address of808* the newly created texture.809*810* @return KTX_SUCCESS on success, other KTX_* enum values on error.811*812* For exceptions, see ktxTexture1_CreateFromStdioStream().813*/814KTX_error_code815ktxTexture1_CreateFromStream(ktxStream* pStream,816ktxTextureCreateFlags createFlags,817ktxTexture1** newTex)818{819KTX_error_code result;820if (newTex == NULL)821return KTX_INVALID_VALUE;822823ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));824if (tex == NULL)825return KTX_OUT_OF_MEMORY;826827result = ktxTexture1_constructFromStream(tex, pStream, createFlags);828if (result == KTX_SUCCESS)829*newTex = (ktxTexture1*)tex;830else {831free(tex);832*newTex = NULL;833}834return result;835}836837/**838* @memberof ktxTexture1839* @~English840* @brief Destroy a ktxTexture1 object.841*842* This frees the memory associated with the texture contents and the memory843* of the ktxTexture1 object. This does @e not delete any OpenGL or Vulkan844* texture objects created by ktxTexture1_GLUpload or ktxTexture1_VkUpload.845*846* @param[in] This pointer to the ktxTexture1 object to destroy847*/848void849ktxTexture1_Destroy(ktxTexture1* This)850{851ktxTexture1_destruct(This);852free(This);853}854855/**856* @memberof ktxTexture @private857* @~English858* @brief Calculate the size of the image data for the specified number859* of levels.860*861* The data size is the sum of the sizes of each level up to the number862* specified and includes any @c mipPadding.863*864* @param[in] This pointer to the ktxTexture object of interest.865* @param[in] levels number of levels whose data size to return.866*867* @return the data size in bytes.868*/869ktx_size_t870ktxTexture1_calcDataSizeLevels(ktxTexture1* This, ktx_uint32_t levels)871{872ktx_uint32_t i;873ktx_size_t dataSize = 0;874875assert(This != NULL);876assert(levels <= This->numLevels);877for (i = 0; i < levels; i++) {878ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,879KTX_FORMAT_VERSION_ONE);880/* mipPadding. NOTE: this adds padding after the last level too. */881#if KTX_GL_UNPACK_ALIGNMENT != 4882dataSize += _KTX_PAD4(levelSize);883#else884dataSize += levelSize;885#endif886}887return dataSize;888}889890/**891* @memberof ktxTexture1 @private892* @~English893*894* @copydoc ktxTexture::ktxTexture_doCalcFaceLodSize895*/896ktx_size_t897ktxTexture1_calcFaceLodSize(ktxTexture1* This, ktx_uint32_t level)898{899return ktxTexture_doCalcFaceLodSize(ktxTexture(This), level,900KTX_FORMAT_VERSION_ONE);901}902903/**904* @memberof ktxTexture @private905* @~English906* @brief Return the offset of a level in bytes from the start of the image907* data in a ktxTexture.908*909* The caclulated size does not include space for storing the @c imageSize910* fields of each mip level.911*912* @param[in] This pointer to the ktxTexture object of interest.913* @param[in] level level whose offset to return.914* @param[in] fv enum specifying format version for which to calculate915* image size.916*917* @return the data size in bytes.918*/919ktx_size_t920ktxTexture1_calcLevelOffset(ktxTexture1* This, ktx_uint32_t level)921{922assert (This != NULL);923assert (level < This->numLevels);924return ktxTexture1_calcDataSizeLevels(This, level);925}926927/**928* @memberof ktxTexture1929* @~English930* @brief Find the offset of an image within a ktxTexture's image data.931*932* As there is no such thing as a 3D cubemap we make the 3rd location parameter933* do double duty.934*935* @param[in] This pointer to the ktxTexture object of interest.936* @param[in] level mip level of the image.937* @param[in] layer array layer of the image.938* @param[in] faceSlice cube map face or depth slice of the image.939* @param[in,out] pOffset pointer to location to store the offset.940*941* @return KTX_SUCCESS on success, other KTX_* enum values on error.942*943* @exception KTX_INVALID_OPERATION944* @p level, @p layer or @p faceSlice exceed the945* dimensions of the texture.946* @exception KTX_INVALID_VALID @p This is NULL.947*/948KTX_error_code949ktxTexture1_GetImageOffset(ktxTexture1* This, ktx_uint32_t level,950ktx_uint32_t layer, ktx_uint32_t faceSlice,951ktx_size_t* pOffset)952{953954if (This == NULL)955return KTX_INVALID_VALUE;956957if (level >= This->numLevels || layer >= This->numLayers)958return KTX_INVALID_OPERATION;959960if (This->isCubemap) {961if (faceSlice >= This->numFaces)962return KTX_INVALID_OPERATION;963} else {964ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level);965if (faceSlice >= maxSlice)966return KTX_INVALID_OPERATION;967}968969// Get the size of the data up to the start of the indexed level.970*pOffset = ktxTexture_calcDataSizeLevels(ktxTexture(This), level);971972// All layers, faces & slices within a level are the same size.973if (layer != 0) {974ktx_size_t layerSize;975layerSize = ktxTexture_layerSize(ktxTexture(This), level,976KTX_FORMAT_VERSION_ONE);977*pOffset += layer * layerSize;978}979if (faceSlice != 0) {980ktx_size_t imageSize;981imageSize = ktxTexture_GetImageSize(ktxTexture(This), level);982#if (KTX_GL_UNPACK_ALIGNMENT != 4)983if (This->isCubemap)984_KTX_PAD4(imageSize); // Account for cubePadding.985#endif986*pOffset += faceSlice * imageSize;987}988989return KTX_SUCCESS;990}991992/**993* @memberof ktxTexture1994* @~English995* @brief Return the total size in bytes of the uncompressed data of a ktxTexture1.996*997* This always returns the value of @c This->dataSize. The function is provided for998* symmetry with ktxTexture2.999*1000* @param[in] This pointer to the ktxTexture1 object of interest.1001* @return The size of the data in the texture.1002*/1003ktx_size_t1004ktxTexture1_GetDataSizeUncompressed(ktxTexture1* This)1005{1006return This->dataSize;1007}10081009/**1010* @memberof ktxTexture11011* @~English1012* @brief Calculate & return the size in bytes of an image at the specified1013* mip level.1014*1015* For arrays, this is the size of a layer, for cubemaps, the size of a face1016* and for 3D textures, the size of a depth slice.1017*1018* The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.1019*1020* @param[in] This pointer to the ktxTexture1 object of interest.1021* @param[in] level level of interest.1022*/1023ktx_size_t1024ktxTexture1_GetImageSize(ktxTexture1* This, ktx_uint32_t level)1025{1026return ktxTexture_calcImageSize(ktxTexture(This), level,1027KTX_FORMAT_VERSION_ONE);1028}10291030/**1031* @memberof ktxTexture11032* @~English1033* @brief Calculate & return the size in bytes of all the images in the specified1034* mip level.1035*1036* For arrays, this is the size of all layers in the level, for cubemaps, the size of all1037* faces in the level and for 3D textures, the size of all depth slices in the level.1038*1039* The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.1040*1041* @param[in] This pointer to the ktxTexture1 object of interest.1042* @param[in] level level of interest.1043*/1044ktx_size_t1045ktxTexture1_GetLevelSize(ktxTexture1* This, ktx_uint32_t level)1046{1047return ktxTexture_calcLevelSize(ktxTexture(This), level,1048KTX_FORMAT_VERSION_ONE);1049}10501051/**1052* @memberof ktxTexture1 @private1053* @~English1054* @brief Return the size of the primitive type of a single color component1055*1056* @param[in] This pointer to the ktxTexture1 object of interest.1057*1058* @return the type size in bytes.1059*/1060ktx_uint32_t1061ktxTexture1_glTypeSize(ktxTexture1* This)1062{1063assert(This != NULL);1064return This->_protected->_typeSize;1065}10661067/**1068* @memberof ktxTexture11069* @~English1070* @brief Iterate over the mip levels in a ktxTexture1 object.1071*1072* This is almost identical to @ref ktxTexture::ktxTexture_IterateLevelFaces1073* "ktxTexture_IterateLevelFaces". The difference is that the blocks of image1074* data for non-array cube maps include all faces of a mip level.1075*1076* This function works even if @p This->pData == 0 so it can be used to1077* obtain offsets and sizes for each level by callers who have loaded the data1078* externally.1079*1080* @param[in] This handle of the 1 opened on the data.1081* @param[in,out] iterCb the address of a callback function which is called1082* with the data for each image block.1083* @param[in,out] userdata the address of application-specific data which is1084* passed to the callback along with the image data.1085*1086* @return KTX_SUCCESS on success, other KTX_* enum values on error. The1087* following are returned directly by this function. @p iterCb may1088* return these for other causes or may return additional errors.1089*1090* @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not1091* decreasing1092* @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.1093*1094*/1095KTX_error_code1096ktxTexture1_IterateLevels(ktxTexture1* This, PFNKTXITERCB iterCb, void* userdata)1097{1098ktx_uint32_t miplevel;1099KTX_error_code result = KTX_SUCCESS;11001101if (This == NULL)1102return KTX_INVALID_VALUE;11031104if (iterCb == NULL)1105return KTX_INVALID_VALUE;11061107for (miplevel = 0; miplevel < This->numLevels; ++miplevel)1108{1109GLsizei width, height, depth;1110ktx_uint32_t levelSize;1111ktx_size_t offset;11121113/* Array textures have the same number of layers at each mip level. */1114width = MAX(1, This->baseWidth >> miplevel);1115height = MAX(1, This->baseHeight >> miplevel);1116depth = MAX(1, This->baseDepth >> miplevel);11171118levelSize = (ktx_uint32_t)ktxTexture_calcLevelSize(ktxTexture(This),1119miplevel,1120KTX_FORMAT_VERSION_ONE);11211122/* All array layers are passed in a group because that is how1123* GL & Vulkan need them. Hence no1124* for (layer = 0; layer < This->numLayers)1125*/1126ktxTexture_GetImageOffset(ktxTexture(This), miplevel, 0, 0, &offset);1127result = iterCb(miplevel, 0, width, height, depth,1128levelSize, This->pData + offset, userdata);1129if (result != KTX_SUCCESS)1130break;1131}11321133return result;1134}11351136/**1137* @memberof ktxTexture11138* @~English1139* @brief Iterate over the images in a ktxTexture1 object while loading the1140* image data.1141*1142* This operates similarly to @ref ktxTexture::ktxTexture_IterateLevelFaces1143* "ktxTexture_IterateLevelFaces" except that it loads the images from the1144* ktxTexture1's source to a temporary buffer while iterating. The callback1145* function must copy the image data if it wishes to preserve it as the1146* temporary buffer is reused for each level and is freed when this function1147* exits.1148*1149* This function is helpful for reducing memory usage when uploading the data1150* to a graphics API.1151*1152* @param[in] This pointer to the ktxTexture1 object of interest.1153* @param[in,out] iterCb the address of a callback function which is called1154* with the data for each image.1155* @param[in,out] userdata the address of application-specific data which is1156* passed to the callback along with the image data.1157*1158* @return KTX_SUCCESS on success, other KTX_* enum values on error. The1159* following are returned directly by this function. @p iterCb may1160* return these for other causes or may return additional errors.1161*1162* @exception KTX_FILE_DATA_ERROR mip level sizes are increasing not1163* decreasing1164* @exception KTX_INVALID_OPERATION the ktxTexture1 was not created from a1165* stream, i.e there is no data to load, or1166* this ktxTexture1's images have already1167* been loaded.1168* @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.1169* @exception KTX_OUT_OF_MEMORY not enough memory to allocate a block to1170* hold the base level image.1171*/1172KTX_error_code1173ktxTexture1_IterateLoadLevelFaces(ktxTexture1* This, PFNKTXITERCB iterCb,1174void* userdata)1175{1176DECLARE_PRIVATE(ktxTexture1);1177struct ktxTexture_protected* prtctd = This->_protected;1178ktxStream* stream = (ktxStream *)&prtctd->_stream;1179ktx_uint32_t dataSize = 0;1180ktx_uint32_t miplevel;1181KTX_error_code result = KTX_SUCCESS;1182void* data = NULL;11831184if (This == NULL)1185return KTX_INVALID_VALUE;11861187if (This->classId != ktxTexture1_c)1188return KTX_INVALID_OPERATION;11891190if (iterCb == NULL)1191return KTX_INVALID_VALUE;11921193if (prtctd->_stream.data.file == NULL)1194// This Texture not created from a stream or images are already loaded.1195return KTX_INVALID_OPERATION;11961197for (miplevel = 0; miplevel < This->numLevels; ++miplevel)1198{1199ktx_uint32_t faceLodSize;1200ktx_uint32_t faceLodSizePadded;1201ktx_uint32_t face;1202ktx_uint32_t innerIterations;1203GLsizei width, height, depth;12041205/* Array textures have the same number of layers at each mip level. */1206width = MAX(1, This->baseWidth >> miplevel);1207height = MAX(1, This->baseHeight >> miplevel);1208depth = MAX(1, This->baseDepth >> miplevel);12091210result = stream->read(stream, &faceLodSize, sizeof(ktx_uint32_t));1211if (result != KTX_SUCCESS) {1212goto cleanup;1213}1214if (private->_needSwap) {1215_ktxSwapEndian32(&faceLodSize, 1);1216}1217#if (KTX_GL_UNPACK_ALIGNMENT != 4)1218faceLodSizePadded = _KTX_PAD4(faceLodSize);1219#else1220faceLodSizePadded = faceLodSize;1221#endif1222if (!data) {1223/* allocate memory sufficient for the base miplevel */1224data = malloc(faceLodSizePadded);1225if (!data) {1226result = KTX_OUT_OF_MEMORY;1227goto cleanup;1228}1229dataSize = faceLodSizePadded;1230}1231else if (dataSize < faceLodSizePadded) {1232/* subsequent miplevels cannot be larger than the base miplevel */1233result = KTX_FILE_DATA_ERROR;1234goto cleanup;1235}12361237/* All array layers are passed in a group because that is how1238* GL & Vulkan need them. Hence no1239* for (layer = 0; layer < This->numLayers)1240*/1241if (This->isCubemap && !This->isArray)1242innerIterations = This->numFaces;1243else1244innerIterations = 1;1245for (face = 0; face < innerIterations; ++face)1246{1247/* And all z_slices are also passed as a group hence no1248* for (z_slice = 0; z_slice < This->depth)1249*/1250result = stream->read(stream, data, faceLodSizePadded);1251if (result != KTX_SUCCESS) {1252goto cleanup;1253}12541255/* Perform endianness conversion on texture data */1256if (private->_needSwap) {1257if (prtctd->_typeSize == 2)1258_ktxSwapEndian16((ktx_uint16_t*)data, faceLodSize / 2);1259else if (prtctd->_typeSize == 4)1260_ktxSwapEndian32((ktx_uint32_t*)data, faceLodSize / 4);1261}12621263result = iterCb(miplevel, face,1264width, height, depth,1265faceLodSize, data, userdata);1266}1267}12681269cleanup:1270free(data);1271// No further need for this.1272stream->destruct(stream);12731274return result;1275}12761277/**1278* @memberof ktxTexture11279* @~English1280* @brief Load all the image data from the ktxTexture1's source.1281*1282* The data is loaded into the provided buffer or to an internally allocated1283* buffer, if @p pBuffer is @c NULL.1284*1285* @param[in] This pointer to the ktxTexture object of interest.1286* @param[in] pBuffer pointer to the buffer in which to load the image data.1287* @param[in] bufSize size of the buffer pointed at by @p pBuffer.1288*1289* @return KTX_SUCCESS on success, other KTX_* enum values on error.1290*1291* @exception KTX_INVALID_VALUE @p This is NULL.1292* @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.1293* @exception KTX_INVALID_OPERATION1294* The data has already been loaded or the1295* ktxTexture was not created from a KTX source.1296* @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.1297*/1298KTX_error_code1299ktxTexture1_LoadImageData(ktxTexture1* This,1300ktx_uint8_t* pBuffer, ktx_size_t bufSize)1301{1302DECLARE_PROTECTED(ktxTexture);1303DECLARE_PRIVATE(ktxTexture1);1304ktx_uint32_t miplevel;1305ktx_uint8_t* pDest;1306ktx_uint8_t* pDestEnd;1307KTX_error_code result = KTX_SUCCESS;13081309if (This == NULL)1310return KTX_INVALID_VALUE;13111312if (prtctd->_stream.data.file == NULL)1313// This Texture not created from a stream or images already loaded;1314return KTX_INVALID_OPERATION;13151316if (pBuffer == NULL) {1317This->pData = malloc(This->dataSize);1318if (This->pData == NULL)1319return KTX_OUT_OF_MEMORY;1320pDest = This->pData;1321pDestEnd = pDest + This->dataSize;1322} else if (bufSize < This->dataSize) {1323return KTX_INVALID_VALUE;1324} else {1325pDest = pBuffer;1326pDestEnd = pBuffer + bufSize;1327}13281329// Need to loop through for correct byte swapping1330for (miplevel = 0; miplevel < This->numLevels; ++miplevel)1331{1332ktx_uint32_t faceLodSize;1333ktx_uint32_t faceLodSizePadded;1334ktx_uint32_t face;1335ktx_uint32_t innerIterations;13361337result = prtctd->_stream.read(&prtctd->_stream, &faceLodSize,1338sizeof(ktx_uint32_t));1339if (result != KTX_SUCCESS) {1340goto cleanup;1341}1342if (private->_needSwap) {1343_ktxSwapEndian32(&faceLodSize, 1);1344}1345#if (KTX_GL_UNPACK_ALIGNMENT != 4)1346faceLodSizePadded = _KTX_PAD4(faceLodSize);1347#else1348faceLodSizePadded = faceLodSize;1349#endif13501351if (This->isCubemap && !This->isArray)1352innerIterations = This->numFaces;1353else1354innerIterations = 1;1355for (face = 0; face < innerIterations; ++face)1356{1357if (pDest + faceLodSizePadded > pDestEnd) {1358result = KTX_INVALID_VALUE;1359goto cleanup;1360}1361result = prtctd->_stream.read(&prtctd->_stream, pDest,1362faceLodSizePadded);1363if (result != KTX_SUCCESS) {1364goto cleanup;1365}13661367/* Perform endianness conversion on texture data */1368if (private->_needSwap) {1369if (prtctd->_typeSize == 2)1370_ktxSwapEndian16((ktx_uint16_t*)pDest, faceLodSize / 2);1371else if (prtctd->_typeSize == 4)1372_ktxSwapEndian32((ktx_uint32_t*)pDest, faceLodSize / 4);1373}13741375pDest += faceLodSizePadded;1376}1377}13781379cleanup:1380// No further need for This->1381prtctd->_stream.destruct(&prtctd->_stream);1382return result;1383}13841385ktx_bool_t1386ktxTexture1_NeedsTranscoding(ktxTexture1* This)1387{1388UNUSED(This);1389return KTX_FALSE;1390}13911392#if !KTX_FEATURE_WRITE13931394/*1395* Stubs for writer functions that return a proper error code1396*/13971398KTX_error_code1399ktxTexture1_SetImageFromMemory(ktxTexture1* This, ktx_uint32_t level,1400ktx_uint32_t layer, ktx_uint32_t faceSlice,1401const ktx_uint8_t* src, ktx_size_t srcSize)1402{1403UNUSED(This);1404UNUSED(level);1405UNUSED(layer);1406UNUSED(faceSlice);1407UNUSED(src);1408UNUSED(srcSize);1409return KTX_INVALID_OPERATION;1410}14111412KTX_error_code1413ktxTexture1_SetImageFromStdioStream(ktxTexture1* This, ktx_uint32_t level,1414ktx_uint32_t layer, ktx_uint32_t faceSlice,1415FILE* src, ktx_size_t srcSize)1416{1417UNUSED(This);1418UNUSED(level);1419UNUSED(layer);1420UNUSED(faceSlice);1421UNUSED(src);1422UNUSED(srcSize);1423return KTX_INVALID_OPERATION;1424}14251426KTX_error_code1427ktxTexture1_WriteToStdioStream(ktxTexture1* This, FILE* dstsstr)1428{1429UNUSED(This);1430UNUSED(dstsstr);1431return KTX_INVALID_OPERATION;1432}14331434KTX_error_code1435ktxTexture1_WriteToNamedFile(ktxTexture1* This, const char* const dstname)1436{1437UNUSED(This);1438UNUSED(dstname);1439return KTX_INVALID_OPERATION;1440}14411442KTX_error_code1443ktxTexture1_WriteToMemory(ktxTexture1* This,1444ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)1445{1446UNUSED(This);1447UNUSED(ppDstBytes);1448UNUSED(pSize);1449return KTX_INVALID_OPERATION;1450}14511452KTX_error_code1453ktxTexture1_WriteToStream(ktxTexture1* This,1454ktxStream* dststr)1455{1456UNUSED(This);1457UNUSED(dststr);1458return KTX_INVALID_OPERATION;1459}14601461#endif14621463/*1464* Initialized here at the end to avoid the need for multiple declarations of1465* these functions.1466*/14671468struct ktxTexture_vtblInt ktxTexture1_vtblInt = {1469(PFNCALCDATASIZELEVELS)ktxTexture1_calcDataSizeLevels,1470(PFNCALCFACELODSIZE)ktxTexture1_calcFaceLodSize,1471(PFNCALCLEVELOFFSET)ktxTexture1_calcLevelOffset1472};14731474struct ktxTexture_vtbl ktxTexture1_vtbl = {1475(PFNKTEXDESTROY)ktxTexture1_Destroy,1476(PFNKTEXGETIMAGEOFFSET)ktxTexture1_GetImageOffset,1477(PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture1_GetDataSizeUncompressed,1478(PFNKTEXGETIMAGESIZE)ktxTexture1_GetImageSize,1479(PFNKTEXGETLEVELSIZE)ktxTexture1_GetLevelSize,1480(PFNKTEXITERATELEVELS)ktxTexture1_IterateLevels,1481(PFNKTEXITERATELOADLEVELFACES)ktxTexture1_IterateLoadLevelFaces,1482(PFNKTEXNEEDSTRANSCODING)ktxTexture1_NeedsTranscoding,1483(PFNKTEXLOADIMAGEDATA)ktxTexture1_LoadImageData,1484(PFNKTEXSETIMAGEFROMMEMORY)ktxTexture1_SetImageFromMemory,1485(PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture1_SetImageFromStdioStream,1486(PFNKTEXWRITETOSTDIOSTREAM)ktxTexture1_WriteToStdioStream,1487(PFNKTEXWRITETONAMEDFILE)ktxTexture1_WriteToNamedFile,1488(PFNKTEXWRITETOMEMORY)ktxTexture1_WriteToMemory,1489(PFNKTEXWRITETOSTREAM)ktxTexture1_WriteToStream,1490};14911492/** @} */1493149414951496