/* -*- tab-width: 4; -*- */1/* vi: set sw=2 ts=4 expandtab: */23/*4* Copyright 2018-2020 Mark Callow.5* SPDX-License-Identifier: Apache-2.06*/78/**9* @internal10* @file11* @~English12*13* @brief ktxTexture implementation.14*15* @author Mark Callow, www.edgewise-consulting.com16*/1718#if defined(_WIN32)19#define _CRT_SECURE_NO_WARNINGS20#ifndef __cplusplus21#undef inline22#define inline __inline23#endif // __cplusplus24#endif2526#include <assert.h>27#include <math.h>28#include <stdlib.h>29#include <string.h>3031#include "ktx.h"32#include "ktxint.h"33#include "formatsize.h"34#include "filestream.h"35#include "memstream.h"36#include "texture1.h"37#include "texture2.h"38#include "unused.h"3940ktx_size_t ktxTexture_GetDataSize(ktxTexture* This);4142static ktx_uint32_t padRow(ktx_uint32_t* rowBytes);4344/**45* @memberof ktxTexture @private46* @~English47* @brief Construct (initialize) a ktxTexture base class instance.48*49* @param[in] This pointer to a ktxTexture-sized block of memory to50* initialize.51* @param[in] createInfo pointer to a ktxTextureCreateInfo struct with52* information describing the texture.53* @param[in] formatSize pointer to a ktxFormatSize giving size information54* about the texture's elements.55*56* @return KTX_SUCCESS on success, other KTX_* enum values on error.57*58* @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a59* valid OpenGL internal format value.60* @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 261* or 3.62* @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in63* @p createInfo is 0.64* @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.65* @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.66* @exception KTX_INVALID_OPERATION67* The <tt>base{Width,Height,Depth}</tt> specified68* in @p createInfo are inconsistent with69* @c numDimensions.70* @exception KTX_INVALID_OPERATION71* @p createInfo is requesting a 3D array or72* 3D cubemap texture.73* @exception KTX_INVALID_OPERATION74* @p createInfo is requesting a cubemap with75* non-square or non-2D images.76* @exception KTX_INVALID_OPERATION77* @p createInfo is requesting more mip levels78* than needed for the specified79* <tt>base{Width,Height,Depth}</tt>.80* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture.81*/82KTX_error_code83ktxTexture_construct(ktxTexture* This,84const ktxTextureCreateInfo* const createInfo,85ktxFormatSize* formatSize)86{87DECLARE_PROTECTED(ktxTexture);8889memset(This, 0, sizeof(*This));90This->_protected = (struct ktxTexture_protected*)malloc(sizeof(*prtctd));91if (!This->_protected)92return KTX_OUT_OF_MEMORY;93prtctd = This->_protected;94memset(prtctd, 0, sizeof(*prtctd));95memcpy(&prtctd->_formatSize, formatSize, sizeof(prtctd->_formatSize));9697This->isCompressed = (formatSize->flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);9899This->orientation.x = KTX_ORIENT_X_RIGHT;100This->orientation.y = KTX_ORIENT_Y_DOWN;101This->orientation.z = KTX_ORIENT_Z_OUT;102103/* Check texture dimensions. KTX files can store 8 types of textures:104* 1D, 2D, 3D, cube, and array variants of these.105*/106if (createInfo->numDimensions < 1 || createInfo->numDimensions > 3)107return KTX_INVALID_VALUE;108109if (createInfo->baseWidth == 0 || createInfo->baseHeight == 0110|| createInfo->baseDepth == 0)111return KTX_INVALID_VALUE;112113switch (createInfo->numDimensions) {114case 1:115if (createInfo->baseHeight > 1 || createInfo->baseDepth > 1)116return KTX_INVALID_OPERATION;117break;118119case 2:120if (createInfo->baseDepth > 1)121return KTX_INVALID_OPERATION;122break;123124case 3:125/* 3D array textures and 3D cubemaps are not supported by either126* OpenGL or Vulkan.127*/128if (createInfo->isArray || createInfo->numFaces != 1129|| createInfo->numLayers != 1)130return KTX_INVALID_OPERATION;131break;132}133This->numDimensions = createInfo->numDimensions;134This->baseWidth = createInfo->baseWidth;135This->baseDepth = createInfo->baseDepth;136This->baseHeight = createInfo->baseHeight;137138if (createInfo->numLayers == 0)139return KTX_INVALID_VALUE;140This->numLayers = createInfo->numLayers;141This->isArray = createInfo->isArray;142143if (createInfo->numFaces == 6) {144if (This->numDimensions != 2) {145/* cube map needs 2D faces */146return KTX_INVALID_OPERATION;147}148if (createInfo->baseWidth != createInfo->baseHeight) {149/* cube maps require square images */150return KTX_INVALID_OPERATION;151}152This->isCubemap = KTX_TRUE;153} else if (createInfo->numFaces != 1) {154/* numFaces must be either 1 or 6 */155return KTX_INVALID_VALUE;156}157This->numFaces = createInfo->numFaces;158159/* Check number of mipmap levels */160if (createInfo->numLevels == 0)161return KTX_INVALID_VALUE;162This->numLevels = createInfo->numLevels;163This->generateMipmaps = createInfo->generateMipmaps;164165if (createInfo->numLevels > 1) {166GLuint max_dim = MAX(MAX(createInfo->baseWidth, createInfo->baseHeight),167createInfo->baseDepth);168if (max_dim < ((GLuint)1 << (This->numLevels - 1)))169{170/* Can't have more mip levels than 1 + log2(max(width, height, depth)) */171return KTX_INVALID_OPERATION;172}173}174175ktxHashList_Construct(&This->kvDataHead);176return KTX_SUCCESS;177}178179/**180* @memberof ktxTexture @private181* @~English182* @brief Construct (initialize) the part of a ktxTexture base class that is183* not related to the stream contents.184*185* @param[in] This pointer to a ktxTexture-sized block of memory to186* initialize.187*188* @return KTX_SUCCESS on success, other KTX_* enum values on error.189*/190KTX_error_code191ktxTexture_constructFromStream(ktxTexture* This, ktxStream* pStream,192ktxTextureCreateFlags createFlags)193{194ktxStream* stream;195UNUSED(createFlags); // Reference to keep compiler happy.196197assert(This != NULL);198assert(pStream->data.mem != NULL);199assert(pStream->type == eStreamTypeFile200|| pStream->type == eStreamTypeMemory201|| pStream->type == eStreamTypeCustom);202203This->_protected = (struct ktxTexture_protected *)204malloc(sizeof(struct ktxTexture_protected));205stream = ktxTexture_getStream(This);206// Copy stream info into struct for later use.207*stream = *pStream;208209This->orientation.x = KTX_ORIENT_X_RIGHT;210This->orientation.y = KTX_ORIENT_Y_DOWN;211This->orientation.z = KTX_ORIENT_Z_OUT;212213return KTX_SUCCESS;214}215216217/**218* @memberof ktxTexture @private219* @~English220* @brief Free the memory associated with the texture contents221*222* @param[in] This pointer to the ktxTextureInt whose texture contents are223* to be freed.224*/225void226ktxTexture_destruct(ktxTexture* This)227{228ktxStream stream = *(ktxTexture_getStream(This));229230if (stream.data.file != NULL)231stream.destruct(&stream);232if (This->kvDataHead != NULL)233ktxHashList_Destruct(&This->kvDataHead);234if (This->kvData != NULL)235free(This->kvData);236if (This->pData != NULL)237free(This->pData);238free(This->_protected);239}240241242/**243* @defgroup reader Reader244* @brief Read KTX-formatted data.245* @{246*/247248typedef enum { KTX1, KTX2 } ktxFileType_;249typedef union {250KTX_header ktx;251KTX_header2 ktx2;252} ktxHeaderUnion_;253254/**255* @memberof ktxTexture @private256* @~English257* @brief Determine if stream data is KTX1 or KTX2.258*259* @param pStream pointer to the ktxStream to examine.260* @param pFileType pointer to a ktxFileType enum where the type of the data261* will be written.262* @param pHeader pointer to a ktxHeaderUnion where the header info. will be263* written.264*/265static KTX_error_code266ktxDetermineFileType_(ktxStream* pStream, ktxFileType_* pFileType,267ktxHeaderUnion_* pHeader)268{269ktx_uint8_t ktx_ident_ref[12] = KTX_IDENTIFIER_REF;270ktx_uint8_t ktx2_ident_ref[12] = KTX2_IDENTIFIER_REF;271KTX_error_code result;272273assert(pStream != NULL && pFileType != NULL);274assert(pStream->data.mem != NULL);275assert(pStream->type == eStreamTypeFile276|| pStream->type == eStreamTypeMemory277|| pStream->type == eStreamTypeCustom);278279result = pStream->read(pStream, pHeader, sizeof(ktx2_ident_ref));280if (result == KTX_SUCCESS) {281#if BIG_ENDIAN282// byte swap the heaader fields283#endif284// Compare identifier, is this a KTX or KTX2 file?285if (!memcmp(pHeader->ktx.identifier, ktx_ident_ref, 12)) {286*pFileType = KTX1;287} else if (!memcmp(pHeader->ktx2.identifier, ktx2_ident_ref, 12)) {288*pFileType = KTX2;289} else {290return KTX_UNKNOWN_FILE_FORMAT;291}292// Read rest of header.293if (*pFileType == KTX1) {294// Read rest of header.295result = pStream->read(pStream, &pHeader->ktx.endianness,296KTX_HEADER_SIZE - sizeof(ktx_ident_ref));297} else {298result = pStream->read(pStream, &pHeader->ktx2.vkFormat,299KTX2_HEADER_SIZE - sizeof(ktx2_ident_ref));300}301}302return result;303}304305/**306* @memberof ktxTexture307* @~English308* @brief Create a ktx1 or ktx2 texture according to the stream309* data.310*311* See @ref ktxTexture1::ktxTexture1_CreateFromStream312* "ktxTexture1_CreateFromStream" or313* @ref ktxTexture2::ktxTexture2_CreateFromStream314* "ktxTexture2_CreateFromStream" for details.315*/316KTX_error_code317ktxTexture_CreateFromStream(ktxStream* pStream,318ktxTextureCreateFlags createFlags,319ktxTexture** newTex)320{321ktxHeaderUnion_ header;322ktxFileType_ fileType;323KTX_error_code result;324ktxTexture* tex;325326result = ktxDetermineFileType_(pStream, &fileType, &header);327if (result != KTX_SUCCESS)328return result;329330if (fileType == KTX1) {331ktxTexture1* tex1 = (ktxTexture1*)malloc(sizeof(ktxTexture1));332if (tex1 == NULL)333return KTX_OUT_OF_MEMORY;334memset(tex1, 0, sizeof(ktxTexture1));335result = ktxTexture1_constructFromStreamAndHeader(tex1, pStream,336&header.ktx,337createFlags);338tex = ktxTexture(tex1);339} else {340ktxTexture2* tex2 = (ktxTexture2*)malloc(sizeof(ktxTexture2));341if (tex2 == NULL)342return KTX_OUT_OF_MEMORY;343memset(tex2, 0, sizeof(ktxTexture2));344result = ktxTexture2_constructFromStreamAndHeader(tex2, pStream,345&header.ktx2,346createFlags);347tex = ktxTexture(tex2);348}349350if (result == KTX_SUCCESS)351*newTex = (ktxTexture*)tex;352else {353free(tex);354*newTex = NULL;355}356return result;357}358359/**360* @memberof ktxTexture361* @~English362* @brief Create a ktxTexture1 or ktxTexture2 from a stdio stream according363* to the stream data.364*365* See @ref ktxTexture1::ktxTexture1_CreateFromStdioStream366* "ktxTexture1_CreateFromStdioStream" or367* @ref ktxTexture2::ktxTexture2_CreateFromStdioStream368* "ktxTexture2_CreateFromStdioStream" for details.369*/370KTX_error_code371ktxTexture_CreateFromStdioStream(FILE* stdioStream,372ktxTextureCreateFlags createFlags,373ktxTexture** newTex)374{375ktxStream stream;376KTX_error_code result;377378if (stdioStream == NULL || newTex == NULL)379return KTX_INVALID_VALUE;380381result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);382if (result == KTX_SUCCESS) {383result = ktxTexture_CreateFromStream(&stream, createFlags, newTex);384}385return result;386}387388/**389* @memberof ktxTexture390* @~English391* @brief Create a ktxTexture1 or ktxTexture2 from a named KTX file according392* to the file contents.393*394* See @ref ktxTexture1::ktxTexture1_CreateFromNamedFile395* "ktxTexture1_CreateFromNamedFile" or396* @ref ktxTexture2::ktxTexture2_CreateFromNamedFile397* "ktxTexture2_CreateFromNamedFile" for details.398*/399KTX_error_code400ktxTexture_CreateFromNamedFile(const char* const filename,401ktxTextureCreateFlags createFlags,402ktxTexture** newTex)403{404KTX_error_code result;405ktxStream stream;406FILE* file;407408if (filename == NULL || newTex == NULL)409return KTX_INVALID_VALUE;410411file = ktxFOpenUTF8(filename, "rb");412if (!file)413return KTX_FILE_OPEN_FAILED;414415result = ktxFileStream_construct(&stream, file, KTX_TRUE);416if (result == KTX_SUCCESS) {417result = ktxTexture_CreateFromStream(&stream, createFlags, newTex);418}419return result;420}421422/**423* @memberof ktxTexture424* @~English425* @brief Create a ktxTexture1 or ktxTexture2 from KTX-formatted data in memory426* according to the data contents.427*428* See @ref ktxTexture1::ktxTexture1_CreateFromMemory429* "ktxTexture1_CreateFromMemory" or430* @ref ktxTexture2::ktxTexture2_CreateFromMemory431* "ktxTexture2_CreateFromMemory" for details.432*/433KTX_error_code434ktxTexture_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,435ktxTextureCreateFlags createFlags,436ktxTexture** newTex)437{438KTX_error_code result;439ktxStream stream;440441if (bytes == NULL || newTex == NULL || size == 0)442return KTX_INVALID_VALUE;443444result = ktxMemStream_construct_ro(&stream, bytes, size);445if (result == KTX_SUCCESS) {446result = ktxTexture_CreateFromStream(&stream, createFlags, newTex);447}448return result;}449450451/**452* @memberof ktxTexture453* @~English454* @brief Return a pointer to the texture image data.455*456* @param[in] This pointer to the ktxTexture object of interest.457*/458ktx_uint8_t*459ktxTexture_GetData(ktxTexture* This)460{461return This->pData;462}463464/**465* @memberof ktxTexture466* @~English467* @brief Return the total size of the texture image data in bytes.468*469* For a ktxTexture2 with supercompressionScheme != KTX_SS_NONE this will470* return the deflated size of the data.471*472* @param[in] This pointer to the ktxTexture object of interest.473*/474ktx_size_t475ktxTexture_GetDataSize(ktxTexture* This)476{477assert(This != NULL);478return This->dataSize;479}480481/**482* @memberof ktxTexture483* @~English484* @brief Return the size in bytes of an elements of a texture's485* images.486*487* For uncompressed textures an element is one texel. For compressed488* textures it is one block.489*490* @param[in] This pointer to the ktxTexture object of interest.491*/492ktx_uint32_t493ktxTexture_GetElementSize(ktxTexture* This)494{495assert (This != NULL);496497return (This->_protected->_formatSize.blockSizeInBits / 8);498}499500/**501* @memberof ktxTexture @private502* @~English503* @brief Calculate & return the size in bytes of an image at the specified504* mip level.505*506* For arrays, this is the size of layer, for cubemaps, the size of a face507* and for 3D textures, the size of a depth slice.508*509* The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.510*511* @param[in] This pointer to the ktxTexture object of interest.512* @param[in] level level of interest.513* @param[in] fv enum specifying format version for which to calculate514* image size.515*/516ktx_size_t517ktxTexture_calcImageSize(ktxTexture* This, ktx_uint32_t level,518ktxFormatVersionEnum fv)519{520DECLARE_PROTECTED(ktxTexture);521struct blockCount {522ktx_uint32_t x, y;523} blockCount;524ktx_uint32_t blockSizeInBytes;525ktx_uint32_t rowBytes;526527assert (This != NULL);528529float levelWidth = (float)(This->baseWidth >> level);530float levelHeight = (float)(This->baseHeight >> level);531// Round up to next whole block. We can't use KTX_PADN because some of532// the block sizes are not powers of 2.533blockCount.x534= (ktx_uint32_t)ceilf(levelWidth / prtctd->_formatSize.blockWidth);535blockCount.y536= (ktx_uint32_t)ceilf(levelHeight / prtctd->_formatSize.blockHeight);537blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x);538blockCount.y = MAX(prtctd->_formatSize.minBlocksY, blockCount.y);539540blockSizeInBytes = prtctd->_formatSize.blockSizeInBits / 8;541542if (prtctd->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT) {543assert(This->isCompressed);544return blockCount.x * blockCount.y * blockSizeInBytes;545} else {546assert(prtctd->_formatSize.blockWidth == 1U547&& prtctd->_formatSize.blockHeight == 1U548&& prtctd->_formatSize.blockDepth == 1U);549rowBytes = blockCount.x * blockSizeInBytes;550if (fv == KTX_FORMAT_VERSION_ONE)551(void)padRow(&rowBytes);552return rowBytes * blockCount.y;553}554}555556/**557* @memberof ktxTexture558* @~English559* @brief Iterate over the levels or faces in a ktxTexture object.560*561* Blocks of image data are passed to an application-supplied callback562* function. This is not a strict per-image iteration. Rather it reflects how563* OpenGL needs the images. For most textures the block of data includes all564* images of a mip level which implies all layers of an array. However, for565* non-array cube map textures the block is a single face of the mip level,566* i.e the callback is called once for each face.567*568* This function works even if @p This->pData == 0 so it can be used to569* obtain offsets and sizes for each level by callers who have loaded the data570* externally.571*572* @param[in] This pointer to the ktxTexture object of interest.573* @param[in,out] iterCb the address of a callback function which is called574* with the data for each image block.575* @param[in,out] userdata the address of application-specific data which is576* passed to the callback along with the image data.577*578* @return KTX_SUCCESS on success, other KTX_* enum values on error. The579* following are returned directly by this function. @p iterCb may580* return these for other causes or may return additional errors.581*582* @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not583* decreasing584* @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.585*586*/587KTX_error_code588ktxTexture_IterateLevelFaces(ktxTexture* This, PFNKTXITERCB iterCb,589void* userdata)590{591ktx_uint32_t miplevel;592KTX_error_code result = KTX_SUCCESS;593594if (This == NULL)595return KTX_INVALID_VALUE;596597if (iterCb == NULL)598return KTX_INVALID_VALUE;599600for (miplevel = 0; miplevel < This->numLevels; ++miplevel)601{602ktx_uint32_t faceLodSize;603ktx_uint32_t face;604ktx_uint32_t innerIterations;605GLsizei width, height, depth;606607/* Array textures have the same number of layers at each mip level. */608width = MAX(1, This->baseWidth >> miplevel);609height = MAX(1, This->baseHeight >> miplevel);610depth = MAX(1, This->baseDepth >> miplevel);611612faceLodSize = (ktx_uint32_t)ktxTexture_calcFaceLodSize(613This, miplevel);614615/* All array layers are passed in a group because that is how616* GL & Vulkan need them. Hence no617* for (layer = 0; layer < This->numLayers)618*/619if (This->isCubemap && !This->isArray)620innerIterations = This->numFaces;621else622innerIterations = 1;623for (face = 0; face < innerIterations; ++face)624{625/* And all z_slices are also passed as a group hence no626* for (slice = 0; slice < This->depth)627*/628ktx_size_t offset;629630ktxTexture_GetImageOffset(This, miplevel, 0, face, &offset);631result = iterCb(miplevel, face,632width, height, depth,633faceLodSize, This->pData + offset, userdata);634635if (result != KTX_SUCCESS)636break;637}638}639640return result;641}642643/**644* @internal645* @brief Calculate and apply the padding needed to comply with646* KTX_GL_UNPACK_ALIGNMENT.647*648* For uncompressed textures, KTX format specifies KTX_GL_UNPACK_ALIGNMENT = 4.649*650* @param[in,out] rowBytes pointer to variable containing the packed no. of651* bytes in a row. The no. of bytes after padding652* is written into this location.653* @return the no. of bytes of padding.654*/655static ktx_uint32_t656padRow(ktx_uint32_t* rowBytes)657{658ktx_uint32_t rowPadding;659660assert (rowBytes != NULL);661662rowPadding = _KTX_PAD_UNPACK_ALIGN_LEN(*rowBytes);663*rowBytes += rowPadding;664return rowPadding;665}666667/**668* @memberof ktxTexture @private669* @~English670* @brief Calculate the size of an array layer at the specified mip level.671*672* The size of a layer is the size of an image * either the number of faces673* or the number of depth slices. This is the size of a layer as needed to674* find the offset within the array of images of a level and layer so the size675* reflects any @c cubePadding.676*677* @param[in] This pointer to the ktxTexture object of interest.678* @param[in] level level whose layer size to return.679*680* @return the layer size in bytes.681*/682ktx_size_t683ktxTexture_layerSize(ktxTexture* This, ktx_uint32_t level,684ktxFormatVersionEnum fv)685{686/*687* As there are no 3D cubemaps, the image's z block count will always be688* 1 for cubemaps and numFaces will always be 1 for 3D textures so the689* multiply is safe. 3D cubemaps, if they existed, would require690* imageSize * (blockCount.z + This->numFaces);691*/692DECLARE_PROTECTED(ktxTexture);693ktx_uint32_t blockCountZ;694ktx_size_t imageSize, layerSize;695696assert (This != NULL);697assert (prtctd->_formatSize.blockDepth != 0);698699blockCountZ = ((This->baseDepth >> level) + prtctd->_formatSize.blockDepth - 1) / prtctd->_formatSize.blockDepth;700blockCountZ = MAX(1, blockCountZ);701imageSize = ktxTexture_calcImageSize(This, level, fv);702layerSize = imageSize * blockCountZ;703if (fv == KTX_FORMAT_VERSION_ONE && KTX_GL_UNPACK_ALIGNMENT != 4) {704if (This->isCubemap && !This->isArray) {705/* cubePadding. NOTE: this adds padding after the last face too. */706layerSize += _KTX_PAD4(layerSize);707}708}709return layerSize * This->numFaces;710}711712/**713* @memberof ktxTexture @private714* @~English715* @brief Calculate the size of the specified mip level.716*717* The size of a level is the size of a layer * the number of layers.718*719* @param[in] This pointer to the ktxTexture object of interest.720* @param[in] level level whose layer size to return.721*722* @return the level size in bytes.723*/724ktx_size_t725ktxTexture_calcLevelSize(ktxTexture* This, ktx_uint32_t level,726ktxFormatVersionEnum fv)727{728assert (This != NULL);729assert (level < This->numLevels);730return ktxTexture_layerSize(This, level, fv) * This->numLayers;731}732733/**734* @memberof ktxTexture @private735* @~English736* @brief Calculate the faceLodSize of the specified mip level.737*738* The faceLodSize of a level for most textures is the size of a level. For739* non-array cube map textures is the size of a face. This is the size that740* must be provided to OpenGL when uploading textures. Faces get uploaded 1741* at a time while all layers of an array or all slices of a 3D texture are742* uploaded together.743*744* @param[in] This pointer to the ktxTexture object of interest.745* @param[in] level level whose layer size to return.746*747* @return the faceLodSize size in bytes.748*/749ktx_size_t750ktxTexture_doCalcFaceLodSize(ktxTexture* This, ktx_uint32_t level,751ktxFormatVersionEnum fv)752{753/*754* For non-array cubemaps this is the size of a face. For everything755* else it is the size of the level.756*/757if (This->isCubemap && !This->isArray)758return ktxTexture_calcImageSize(This, level, fv);759else760return ktxTexture_calcLevelSize(This, level, fv);761}762763764/**765* @memberof ktxTexture @private766* @~English767* @brief Return the number of bytes needed to store all the image data for768* a ktxTexture.769*770* The caclulated size does not include space for storing the @c imageSize771* fields of each mip level.772*773* @param[in] This pointer to the ktxTexture object of interest.774* @param[in] fv enum specifying format version for which to calculate775* image size.776*777* @return the data size in bytes.778*/779ktx_size_t780ktxTexture_calcDataSizeTexture(ktxTexture* This)781{782assert (This != NULL);783return ktxTexture_calcDataSizeLevels(This, This->numLevels);784}785786/**787* @memberof ktxTexture @private788* @~English789* @brief Get information about rows of an uncompresssed texture image at a790* specified level.791*792* For an image at @p level of a ktxTexture provide the number of rows, the793* packed (unpadded) number of bytes in a row and the padding necessary to794* comply with KTX_GL_UNPACK_ALIGNMENT.795*796* @param[in] This pointer to the ktxTexture object of interest.797* @param[in] level level of interest.798* @param[in,out] numRows pointer to location to store the number of rows.799* @param[in,out] pRowLengthBytes pointer to location to store number of bytes800* in a row.801* @param[in.out] pRowPadding pointer to location to store the number of bytes802* of padding.803*/804void805ktxTexture_rowInfo(ktxTexture* This, ktx_uint32_t level,806ktx_uint32_t* numRows, ktx_uint32_t* pRowLengthBytes,807ktx_uint32_t* pRowPadding)808{809DECLARE_PROTECTED(ktxTexture);810struct blockCount {811ktx_uint32_t x;812} blockCount;813814assert (This != NULL);815816assert(!This->isCompressed);817assert(prtctd->_formatSize.blockWidth == 1U818&& prtctd->_formatSize.blockHeight == 1U819&& prtctd->_formatSize.blockDepth == 1U);820821blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth) >> level);822*numRows = MAX(1, (This->baseHeight / prtctd->_formatSize.blockHeight) >> level);823824*pRowLengthBytes = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8;825*pRowPadding = padRow(pRowLengthBytes);826}827828/**829* @memberof ktxTexture830* @~English831* @brief Return pitch between rows of a texture image level in bytes.832*833* For uncompressed textures the pitch is the number of bytes between834* rows of texels. For compressed textures it is the number of bytes835* between rows of blocks. The value is padded to GL_UNPACK_ALIGNMENT,836* if necessary. For all currently known compressed formats padding837* will not be necessary.838*839* @param[in] This pointer to the ktxTexture object of interest.840* @param[in] level level of interest.841*842* @return the row pitch in bytes.843*/844ktx_uint32_t845ktxTexture_GetRowPitch(ktxTexture* This, ktx_uint32_t level)846{847DECLARE_PROTECTED(ktxTexture)848struct blockCount {849ktx_uint32_t x;850} blockCount;851ktx_uint32_t pitch;852853blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth) >> level);854pitch = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8;855(void)padRow(&pitch);856857return pitch;858}859860/**861* @memberof ktxTexture @private862* @~English863* @brief Query if a ktxTexture has an active stream.864*865* Tests if a ktxTexture has unread image data. The internal stream is closed866* once all the images have been read.867*868* @param[in] This pointer to the ktxTexture object of interest.869*870* @return KTX_TRUE if there is an active stream, KTX_FALSE otherwise.871*/872ktx_bool_t873ktxTexture_isActiveStream(ktxTexture* This)874{875assert(This != NULL);876ktxStream* stream = ktxTexture_getStream(This);877return stream->data.file != NULL;878}879880/** @} */881882883884