| 1 | /* -*- tab-width: 4; -*- */ | 
| 2 | /* vi: set sw=2 ts=4 expandtab: */ | 
| 3 |  | 
| 4 | /* | 
| 5 |  * Copyright 2018-2020 Mark Callow. | 
| 6 |  * SPDX-License-Identifier: Apache-2.0 | 
| 7 |  */ | 
| 8 |  | 
| 9 | /** | 
| 10 |  * @internal | 
| 11 |  * @file writer.c | 
| 12 |  * @~English | 
| 13 |  * | 
| 14 |  * @brief ktxTexture implementation. | 
| 15 |  * | 
| 16 |  * @author Mark Callow, www.edgewise-consulting.com | 
| 17 |  */ | 
| 18 |  | 
| 19 | #if defined(_WIN32) | 
| 20 |   #define _CRT_SECURE_NO_WARNINGS | 
| 21 |   #ifndef __cplusplus | 
| 22 |     #undef inline | 
| 23 |     #define inline __inline | 
| 24 |   #endif // __cplusplus | 
| 25 | #endif | 
| 26 |  | 
| 27 | #include <assert.h> | 
| 28 | #include <math.h> | 
| 29 | #include <stdlib.h> | 
| 30 | #include <string.h> | 
| 31 |  | 
| 32 | #include "ktx.h" | 
| 33 | #include "ktxint.h" | 
| 34 | #include "formatsize.h" | 
| 35 | #include "filestream.h" | 
| 36 | #include "memstream.h" | 
| 37 | #include "texture1.h" | 
| 38 | #include "texture2.h" | 
| 39 | #include "unused.h" | 
| 40 |  | 
| 41 | ktx_size_t ktxTexture_GetDataSize(ktxTexture* This); | 
| 42 |  | 
| 43 | static ktx_uint32_t padRow(ktx_uint32_t* rowBytes); | 
| 44 |  | 
| 45 | /** | 
| 46 |  * @memberof ktxTexture @private | 
| 47 |  * @~English | 
| 48 |  * @brief Construct (initialize) a ktxTexture base class instance. | 
| 49 |  * | 
| 50 |  * @param[in] This pointer to a ktxTexture-sized block of memory to | 
| 51 |  *                 initialize. | 
| 52 |  * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with | 
| 53 |  *                       information describing the texture. | 
| 54 |  * @param[in] formatSize pointer to a ktxFormatSize giving size information | 
| 55 |  *                       about the texture's elements. | 
| 56 |  * | 
| 57 |  * @return      KTX_SUCCESS on success, other KTX_* enum values on error. | 
| 58 |  * | 
| 59 |  * @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a | 
| 60 |  *                              valid OpenGL internal format value. | 
| 61 |  * @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2 | 
| 62 |  *                              or 3. | 
| 63 |  * @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in | 
| 64 |  *                              @p createInfo is 0. | 
| 65 |  * @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6. | 
| 66 |  * @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0. | 
| 67 |  * @exception KTX_INVALID_OPERATION | 
| 68 |  *                              The <tt>base{Width,Height,Depth}</tt> specified | 
| 69 |  *                              in @p createInfo are inconsistent with | 
| 70 |  *                              @c numDimensions. | 
| 71 |  * @exception KTX_INVALID_OPERATION | 
| 72 |  *                              @p createInfo is requesting a 3D array or | 
| 73 |  *                              3D cubemap texture. | 
| 74 |  * @exception KTX_INVALID_OPERATION | 
| 75 |  *                              @p createInfo is requesting a cubemap with | 
| 76 |  *                              non-square or non-2D images. | 
| 77 |  * @exception KTX_INVALID_OPERATION | 
| 78 |  *                              @p createInfo is requesting more mip levels | 
| 79 |  *                              than needed for the specified | 
| 80 |  *                              <tt>base{Width,Height,Depth}</tt>. | 
| 81 |  * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture. | 
| 82 |  */ | 
| 83 | KTX_error_code | 
| 84 | ktxTexture_construct(ktxTexture* This, ktxTextureCreateInfo* createInfo, | 
| 85 |                      ktxFormatSize* formatSize) | 
| 86 | { | 
| 87 |     DECLARE_PROTECTED(ktxTexture); | 
| 88 |  | 
| 89 |     memset(This, 0, sizeof(*This)); | 
| 90 |     This->_protected = (struct ktxTexture_protected*)malloc(sizeof(*prtctd)); | 
| 91 |     if (!This->_protected) | 
| 92 |         return KTX_OUT_OF_MEMORY; | 
| 93 |     prtctd = This->_protected; | 
| 94 |     memset(prtctd, 0, sizeof(*prtctd)); | 
| 95 |     memcpy(&prtctd->_formatSize, formatSize, sizeof(prtctd->_formatSize)); | 
| 96 |  | 
| 97 |     This->isCompressed = (formatSize->flags & KTX_FORMAT_SIZE_COMPRESSED_BIT); | 
| 98 |  | 
| 99 |     This->orientation.x = KTX_ORIENT_X_RIGHT; | 
| 100 |     This->orientation.y = KTX_ORIENT_Y_DOWN; | 
| 101 |     This->orientation.z = KTX_ORIENT_Z_OUT; | 
| 102 |  | 
| 103 |     /* Check texture dimensions. KTX files can store 8 types of textures: | 
| 104 |      * 1D, 2D, 3D, cube, and array variants of these. | 
| 105 |      */ | 
| 106 |     if (createInfo->numDimensions < 1 || createInfo->numDimensions > 3) | 
| 107 |         return KTX_INVALID_VALUE; | 
| 108 |  | 
| 109 |     if (createInfo->baseWidth == 0 || createInfo->baseHeight == 0 | 
| 110 |         || createInfo->baseDepth == 0) | 
| 111 |         return KTX_INVALID_VALUE; | 
| 112 |  | 
| 113 |     switch (createInfo->numDimensions) { | 
| 114 |       case 1: | 
| 115 |         if (createInfo->baseHeight > 1 || createInfo->baseDepth > 1) | 
| 116 |             return KTX_INVALID_OPERATION; | 
| 117 |         break; | 
| 118 |  | 
| 119 |       case 2: | 
| 120 |         if (createInfo->baseDepth > 1) | 
| 121 |             return KTX_INVALID_OPERATION; | 
| 122 |         break; | 
| 123 |  | 
| 124 |       case 3: | 
| 125 |         /* 3D array textures and 3D cubemaps are not supported by either | 
| 126 |          * OpenGL or Vulkan. | 
| 127 |          */ | 
| 128 |         if (createInfo->isArray || createInfo->numFaces != 1 | 
| 129 |             || createInfo->numLayers != 1) | 
| 130 |             return KTX_INVALID_OPERATION; | 
| 131 |         break; | 
| 132 |     } | 
| 133 |     This->numDimensions = createInfo->numDimensions; | 
| 134 |     This->baseWidth = createInfo->baseWidth; | 
| 135 |     This->baseDepth = createInfo->baseDepth; | 
| 136 |     This->baseHeight = createInfo->baseHeight; | 
| 137 |  | 
| 138 |     if (createInfo->numLayers == 0) | 
| 139 |         return KTX_INVALID_VALUE; | 
| 140 |     This->numLayers = createInfo->numLayers; | 
| 141 |     This->isArray = createInfo->isArray; | 
| 142 |  | 
| 143 |     if (createInfo->numFaces == 6) { | 
| 144 |         if (This->numDimensions != 2) { | 
| 145 |             /* cube map needs 2D faces */ | 
| 146 |             return KTX_INVALID_OPERATION; | 
| 147 |         } | 
| 148 |         if (createInfo->baseWidth != createInfo->baseHeight) { | 
| 149 |             /* cube maps require square images */ | 
| 150 |             return KTX_INVALID_OPERATION; | 
| 151 |         } | 
| 152 |         This->isCubemap = KTX_TRUE; | 
| 153 |     } else if (createInfo->numFaces != 1) { | 
| 154 |         /* numFaces must be either 1 or 6 */ | 
| 155 |         return KTX_INVALID_VALUE; | 
| 156 |     } | 
| 157 |     This->numFaces = createInfo->numFaces; | 
| 158 |  | 
| 159 |     /* Check number of mipmap levels */ | 
| 160 |     if (createInfo->numLevels == 0) | 
| 161 |         return KTX_INVALID_VALUE; | 
| 162 |     This->numLevels = createInfo->numLevels; | 
| 163 |     This->generateMipmaps = createInfo->generateMipmaps; | 
| 164 |  | 
| 165 |     if (createInfo->numLevels > 1) { | 
| 166 |         GLuint max_dim = MAX(MAX(createInfo->baseWidth, createInfo->baseHeight), | 
| 167 |                              createInfo->baseDepth); | 
| 168 |         if (max_dim < ((GLuint)1 << (This->numLevels - 1))) | 
| 169 |         { | 
| 170 |             /* Can't have more mip levels than 1 + log2(max(width, height, depth)) */ | 
| 171 |             return KTX_INVALID_OPERATION; | 
| 172 |         } | 
| 173 |     } | 
| 174 |  | 
| 175 |     ktxHashList_Construct(&This->kvDataHead); | 
| 176 |     return KTX_SUCCESS; | 
| 177 | } | 
| 178 |  | 
| 179 | /** | 
| 180 |  * @memberof ktxTexture @private | 
| 181 |  * @~English | 
| 182 |  * @brief Construct (initialize) the part of a ktxTexture base class that is | 
| 183 |  *        not related to the stream contents. | 
| 184 |  * | 
| 185 |  * @param[in] This pointer to a ktxTexture-sized block of memory to | 
| 186 |  *                 initialize. | 
| 187 |  * | 
| 188 |  * @return      KTX_SUCCESS on success, other KTX_* enum values on error. | 
| 189 |  */ | 
| 190 | KTX_error_code | 
| 191 | ktxTexture_constructFromStream(ktxTexture* This, ktxStream* pStream, | 
| 192 |                                ktxTextureCreateFlags createFlags) | 
| 193 | { | 
| 194 |     ktxStream* stream; | 
| 195 |     UNUSED(createFlags); // Reference to keep compiler happy. | 
| 196 |  | 
| 197 |     assert(This != NULL); | 
| 198 |     assert(pStream->data.mem != NULL); | 
| 199 |     assert(pStream->type == eStreamTypeFile | 
| 200 |            || pStream->type == eStreamTypeMemory | 
| 201 |            || pStream->type == eStreamTypeCustom); | 
| 202 |  | 
| 203 |     This->_protected = (struct ktxTexture_protected *) | 
| 204 |                                 malloc(sizeof(struct ktxTexture_protected)); | 
| 205 |     stream = ktxTexture_getStream(This); | 
| 206 |     // Copy stream info into struct for later use. | 
| 207 |     *stream = *pStream; | 
| 208 |  | 
| 209 |     This->orientation.x = KTX_ORIENT_X_RIGHT; | 
| 210 |     This->orientation.y = KTX_ORIENT_Y_DOWN; | 
| 211 |     This->orientation.z = KTX_ORIENT_Z_OUT; | 
| 212 |  | 
| 213 |     return KTX_SUCCESS; | 
| 214 | } | 
| 215 |  | 
| 216 |  | 
| 217 | /** | 
| 218 |  * @memberof ktxTexture @private | 
| 219 |  * @~English | 
| 220 |  * @brief Free the memory associated with the texture contents | 
| 221 |  * | 
| 222 |  * @param[in] This pointer to the ktxTextureInt whose texture contents are | 
| 223 |  *                 to be freed. | 
| 224 |  */ | 
| 225 | void | 
| 226 | ktxTexture_destruct(ktxTexture* This) | 
| 227 | { | 
| 228 |     ktxStream stream = *(ktxTexture_getStream(This)); | 
| 229 |  | 
| 230 |     if (stream.data.file != NULL) | 
| 231 |         stream.destruct(&stream); | 
| 232 |     if (This->kvDataHead != NULL) | 
| 233 |         ktxHashList_Destruct(&This->kvDataHead); | 
| 234 |     if (This->kvData != NULL) | 
| 235 |         free(This->kvData); | 
| 236 |     if (This->pData != NULL) | 
| 237 |         free(This->pData); | 
| 238 |     free(This->_protected); | 
| 239 | } | 
| 240 |  | 
| 241 |  | 
| 242 | /** | 
| 243 |  * @defgroup reader Reader | 
| 244 |  * @brief Read KTX-formatted data. | 
| 245 |  * @{ | 
| 246 |  */ | 
| 247 |  | 
| 248 | typedef enum { KTX1, KTX2 } ktxFileType_; | 
| 249 | typedef union { | 
| 250 |     KTX_header ktx; | 
| 251 |     KTX_header2 ktx2; | 
| 252 | } ; | 
| 253 |  | 
| 254 | /** | 
| 255 |  * @memberof ktxTexture @private | 
| 256 |  * @~English | 
| 257 |  * @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 data | 
| 261 |  *                  will be written. | 
| 262 |  * @param pHeader   pointer to a ktxHeaderUnion where the header info. will be | 
| 263 |  *                  written. | 
| 264 |  */ | 
| 265 | static KTX_error_code | 
| 266 | ktxDetermineFileType_(ktxStream* pStream, ktxFileType_* pFileType, | 
| 267 |                       ktxHeaderUnion_* ) | 
| 268 | { | 
| 269 |     ktx_uint8_t ktx_ident_ref[12] = KTX_IDENTIFIER_REF; | 
| 270 |     ktx_uint8_t ktx2_ident_ref[12] = KTX2_IDENTIFIER_REF; | 
| 271 |     KTX_error_code result; | 
| 272 |  | 
| 273 |     assert(pStream != NULL && pFileType != NULL); | 
| 274 |     assert(pStream->data.mem != NULL); | 
| 275 |     assert(pStream->type == eStreamTypeFile | 
| 276 |            || pStream->type == eStreamTypeMemory | 
| 277 |            || pStream->type == eStreamTypeCustom); | 
| 278 |  | 
| 279 |     result = pStream->read(pStream, pHeader, sizeof(ktx2_ident_ref)); | 
| 280 |     if (result == KTX_SUCCESS) { | 
| 281 | #if BIG_ENDIAN | 
| 282 |         // byte swap the heaader fields | 
| 283 | #endif | 
| 284 |         // Compare identifier, is this a KTX  or KTX2 file? | 
| 285 |         if (!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 { | 
| 290 |                 return KTX_UNKNOWN_FILE_FORMAT; | 
| 291 |         } | 
| 292 |         // Read rest of header. | 
| 293 |         if (*pFileType == KTX1) { | 
| 294 |             // Read rest of header. | 
| 295 |             result = pStream->read(pStream, &pHeader->ktx.endianness, | 
| 296 |                                   KTX_HEADER_SIZE - sizeof(ktx_ident_ref)); | 
| 297 |         } else { | 
| 298 |            result = pStream->read(pStream, &pHeader->ktx2.vkFormat, | 
| 299 |                                  KTX2_HEADER_SIZE - sizeof(ktx2_ident_ref)); | 
| 300 |         } | 
| 301 |     } | 
| 302 |     return result; | 
| 303 | } | 
| 304 |  | 
| 305 | /** | 
| 306 |  * @memberof ktxTexture | 
| 307 |  * @~English | 
| 308 |  * @brief Construct (initialize) a ktx1 or ktx2 texture according to the stream | 
| 309 |  *        data. | 
| 310 |  * | 
| 311 |  * @copydetails ktxTexture_CreateFromStdioStream | 
| 312 |  */ | 
| 313 | KTX_error_code | 
| 314 | ktxTexture_CreateFromStream(ktxStream* pStream, | 
| 315 |                             ktxTextureCreateFlags createFlags, | 
| 316 |                             ktxTexture** newTex) | 
| 317 | { | 
| 318 |     ktxHeaderUnion_ ; | 
| 319 |     ktxFileType_ fileType; | 
| 320 |     KTX_error_code result; | 
| 321 |     ktxTexture* tex; | 
| 322 |  | 
| 323 |     result = ktxDetermineFileType_(pStream, &fileType, &header); | 
| 324 |     if (result != KTX_SUCCESS) | 
| 325 |         return result; | 
| 326 |  | 
| 327 |     if (fileType == KTX1) { | 
| 328 |         ktxTexture1* tex1 = (ktxTexture1*)malloc(sizeof(ktxTexture1)); | 
| 329 |         if (tex1 == NULL) | 
| 330 |             return KTX_OUT_OF_MEMORY; | 
| 331 |         memset(tex1, 0, sizeof(ktxTexture1)); | 
| 332 |         result = ktxTexture1_constructFromStreamAndHeader(tex1, pStream, | 
| 333 |                                                           &header.ktx, | 
| 334 |                                                           createFlags); | 
| 335 |         tex = ktxTexture(tex1); | 
| 336 |     } else { | 
| 337 |         ktxTexture2* tex2 = (ktxTexture2*)malloc(sizeof(ktxTexture2)); | 
| 338 |         if (tex2 == NULL) | 
| 339 |             return KTX_OUT_OF_MEMORY; | 
| 340 |         memset(tex2, 0, sizeof(ktxTexture2)); | 
| 341 |         result = ktxTexture2_constructFromStreamAndHeader(tex2, pStream, | 
| 342 |                                                           &header.ktx2, | 
| 343 |                                                           createFlags); | 
| 344 |         tex = ktxTexture(tex2); | 
| 345 |     } | 
| 346 |  | 
| 347 |     if (result == KTX_SUCCESS) | 
| 348 |         *newTex = (ktxTexture*)tex; | 
| 349 |     else { | 
| 350 |         free(tex); | 
| 351 |         *newTex = NULL; | 
| 352 |     } | 
| 353 |     return result; | 
| 354 | } | 
| 355 |  | 
| 356 | /** | 
| 357 |  * @memberof ktxTexture | 
| 358 |  * @~English | 
| 359 |  * @brief Create a ktxTexture1 or ktxTexture2 from a stdio stream according | 
| 360 |  *        to the stream data. | 
| 361 |  * | 
| 362 |  * @copydetails ktxTexture1_CreateFromStdioStream() | 
| 363 |  */ | 
| 364 | KTX_error_code | 
| 365 | ktxTexture_CreateFromStdioStream(FILE* stdioStream, | 
| 366 |                                  ktxTextureCreateFlags createFlags, | 
| 367 |                                  ktxTexture** newTex) | 
| 368 | { | 
| 369 |     ktxStream stream; | 
| 370 |     KTX_error_code result; | 
| 371 |  | 
| 372 |     if (stdioStream == NULL || newTex == NULL) | 
| 373 |         return KTX_INVALID_VALUE; | 
| 374 |  | 
| 375 |     result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE); | 
| 376 |     if (result == KTX_SUCCESS) { | 
| 377 |         result = ktxTexture_CreateFromStream(&stream, createFlags, newTex); | 
| 378 |     } | 
| 379 |     return result; | 
| 380 | } | 
| 381 |  | 
| 382 | /** | 
| 383 |  * @memberof ktxTexture | 
| 384 |  * @~English | 
| 385 |  * @brief Create a ktxTexture1 or ktxTexture2 from a named KTX file according | 
| 386 |  *        to the file contents. | 
| 387 |  * | 
| 388 |  * The address of a newly created ktxTexture reflecting the contents of the | 
| 389 |  * file is written to the location pointed at by @p newTex. | 
| 390 |  * | 
| 391 |  * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, | 
| 392 |  * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This | 
| 393 |  * will minimize memory usage by allowing, for example, loading the images | 
| 394 |  * directly from the source into a Vulkan staging buffer. | 
| 395 |  * | 
| 396 |  * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is | 
| 397 |  * provided solely to enable implementation of the @e libktx v1 API on top of | 
| 398 |  * ktxTexture. | 
| 399 |  * | 
| 400 |  * @param[in] filename    pointer to a char array containing the file name. | 
| 401 |  * @param[in] createFlags bitmask requesting specific actions during creation. | 
| 402 |  * @param[in,out] newTex  pointer to a location in which store the address of | 
| 403 |  *                        the newly created texture. | 
| 404 |  * | 
| 405 |  * @return      KTX_SUCCESS on success, other KTX_* enum values on error. | 
| 406 |  | 
| 407 |  * @exception KTX_FILE_OPEN_FAILED The file could not be opened. | 
| 408 |  * @exception KTX_INVALID_VALUE @p filename is @c NULL. | 
| 409 |  * | 
| 410 |  * For other exceptions, see ktxTexture_CreateFromStdioStream(). | 
| 411 |  */ | 
| 412 | KTX_error_code | 
| 413 | ktxTexture_CreateFromNamedFile(const char* const filename, | 
| 414 |                                ktxTextureCreateFlags createFlags, | 
| 415 |                                ktxTexture** newTex) | 
| 416 | { | 
| 417 |     KTX_error_code result; | 
| 418 |     ktxStream stream; | 
| 419 |     FILE* file; | 
| 420 |  | 
| 421 |     if (filename == NULL || newTex == NULL) | 
| 422 |         return KTX_INVALID_VALUE; | 
| 423 |  | 
| 424 |     file = fopen(filename, "rb" ); | 
| 425 |     if (!file) | 
| 426 |        return KTX_FILE_OPEN_FAILED; | 
| 427 |  | 
| 428 |     result = ktxFileStream_construct(&stream, file, KTX_TRUE); | 
| 429 |     if (result == KTX_SUCCESS) { | 
| 430 |         result = ktxTexture_CreateFromStream(&stream, createFlags, newTex); | 
| 431 |     } | 
| 432 |     return result; | 
| 433 | } | 
| 434 |  | 
| 435 | /** | 
| 436 |  * @memberof ktxTexture | 
| 437 |  * @~English | 
| 438 |  * @brief Create a ktxTexture1 or ktxTexture2 from KTX-formatted data in memory | 
| 439 |  *        according to the data contents. | 
| 440 |  * | 
| 441 |  * The address of a newly created ktxTexture reflecting the contents of the | 
| 442 |  * serialized KTX data is written to the location pointed at by @p newTex. | 
| 443 |  * | 
| 444 |  * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set, | 
| 445 |  * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This | 
| 446 |  * will minimize memory usage by allowing, for example, loading the images | 
| 447 |  * directly from the source into a Vulkan staging buffer. | 
| 448 |  * | 
| 449 |  * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is | 
| 450 |  * provided solely to enable implementation of the @e libktx v1 API on top of | 
| 451 |  * ktxTexture. | 
| 452 |  * | 
| 453 |  * @param[in] bytes pointer to the memory containing the serialized KTX data. | 
| 454 |  * @param[in] size  length of the KTX data in bytes. | 
| 455 |  * @param[in] createFlags bitmask requesting specific actions during creation. | 
| 456 |  * @param[in,out] newTex  pointer to a location in which store the address of | 
| 457 |  *                        the newly created texture. | 
| 458 |  * | 
| 459 |  * @return      KTX_SUCCESS on success, other KTX_* enum values on error. | 
| 460 |  * | 
| 461 |  * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0. | 
| 462 |  * | 
| 463 |  * For other exceptions, see ktxTexture_CreateFromStdioStream(). | 
| 464 |  */ | 
| 465 | KTX_error_code | 
| 466 | ktxTexture_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, | 
| 467 |                             ktxTextureCreateFlags createFlags, | 
| 468 |                             ktxTexture** newTex) | 
| 469 | { | 
| 470 |     KTX_error_code result; | 
| 471 |     ktxStream stream; | 
| 472 |  | 
| 473 |     if (bytes == NULL || newTex == NULL || size == 0) | 
| 474 |         return KTX_INVALID_VALUE; | 
| 475 |  | 
| 476 |     result = ktxMemStream_construct_ro(&stream, bytes, size); | 
| 477 |     if (result == KTX_SUCCESS) { | 
| 478 |         result = ktxTexture_CreateFromStream(&stream, createFlags, newTex); | 
| 479 |     } | 
| 480 |     return result;} | 
| 481 |  | 
| 482 |  | 
| 483 | /** | 
| 484 |  * @memberof ktxTexture | 
| 485 |  * @~English | 
| 486 |  * @brief Return a pointer to the texture image data. | 
| 487 |  * | 
| 488 |  * @param[in] This pointer to the ktxTexture object of interest. | 
| 489 |  */ | 
| 490 | ktx_uint8_t* | 
| 491 | ktxTexture_GetData(ktxTexture* This) | 
| 492 | { | 
| 493 |     return This->pData; | 
| 494 | } | 
| 495 |  | 
| 496 | /** | 
| 497 |  * @memberof ktxTexture | 
| 498 |  * @~English | 
| 499 |  * @brief Return the total size of the texture image data in bytes. | 
| 500 |  * | 
| 501 |  * For a ktxTexture2 with supercompressionScheme != KTX_SS_NONE this will | 
| 502 |  * return the deflated size of the data. | 
| 503 |  * | 
| 504 |  * @param[in] This pointer to the ktxTexture object of interest. | 
| 505 |  */ | 
| 506 | ktx_size_t | 
| 507 | ktxTexture_GetDataSize(ktxTexture* This) | 
| 508 | { | 
| 509 |     assert(This != NULL); | 
| 510 |     return This->dataSize; | 
| 511 | } | 
| 512 |  | 
| 513 | /** | 
| 514 |  * @memberof ktxTexture | 
| 515 |  * @~English | 
| 516 |  * @brief Return the size in bytes of an elements of a texture's | 
| 517 |  *        images. | 
| 518 |  * | 
| 519 |  * For uncompressed textures an element is one texel. For compressed | 
| 520 |  * textures it is one block. | 
| 521 |  * | 
| 522 |  * @param[in]     This     pointer to the ktxTexture object of interest. | 
| 523 |  */ | 
| 524 | ktx_uint32_t | 
| 525 | ktxTexture_GetElementSize(ktxTexture* This) | 
| 526 | { | 
| 527 |     assert (This != NULL); | 
| 528 |  | 
| 529 |     return (This->_protected->_formatSize.blockSizeInBits / 8); | 
| 530 | } | 
| 531 |  | 
| 532 | /** | 
| 533 |  * @memberof ktxTexture @private | 
| 534 |  * @~English | 
| 535 |  * @brief Calculate & return the size in bytes of an image at the specified | 
| 536 |  *        mip level. | 
| 537 |  * | 
| 538 |  * For arrays, this is the size of layer, for cubemaps, the size of a face | 
| 539 |  * and for 3D textures, the size of a depth slice. | 
| 540 |  * | 
| 541 |  * The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT. | 
| 542 |  * | 
| 543 |  * @param[in]     This     pointer to the ktxTexture object of interest. | 
| 544 |  * @param[in]     level    level of interest. | 
| 545 |  * @param[in]     fv       enum specifying format version for which to calculate | 
| 546 |  *                         image size. | 
| 547 |  */ | 
| 548 | ktx_size_t | 
| 549 | ktxTexture_calcImageSize(ktxTexture* This, ktx_uint32_t level, | 
| 550 |                          ktxFormatVersionEnum fv) | 
| 551 | { | 
| 552 |     DECLARE_PROTECTED(ktxTexture); | 
| 553 |     struct blockCount { | 
| 554 |         ktx_uint32_t x, y; | 
| 555 |     } blockCount; | 
| 556 |     ktx_uint32_t blockSizeInBytes; | 
| 557 |     ktx_uint32_t rowBytes; | 
| 558 |  | 
| 559 |     assert (This != NULL); | 
| 560 |  | 
| 561 |     float levelWidth  = (float)(This->baseWidth >> level); | 
| 562 |     float levelHeight = (float)(This->baseHeight >> level); | 
| 563 |     // Round up to next whole block. We can't use KTX_PADN because some of | 
| 564 |     // the block sizes are not powers of 2. | 
| 565 |     blockCount.x | 
| 566 |         = (ktx_uint32_t)ceilf(levelWidth / prtctd->_formatSize.blockWidth); | 
| 567 |     blockCount.y | 
| 568 |         = (ktx_uint32_t)ceilf(levelHeight / prtctd->_formatSize.blockHeight); | 
| 569 |     blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x); | 
| 570 |     blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y); | 
| 571 |  | 
| 572 |     blockSizeInBytes = prtctd->_formatSize.blockSizeInBits / 8; | 
| 573 |  | 
| 574 |     if (prtctd->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT) { | 
| 575 |         assert(This->isCompressed); | 
| 576 |         return blockCount.x * blockCount.y * blockSizeInBytes; | 
| 577 |     } else { | 
| 578 |         assert(prtctd->_formatSize.blockWidth == 1U | 
| 579 |                && prtctd->_formatSize.blockHeight == 1U | 
| 580 |                && prtctd->_formatSize.blockDepth == 1U); | 
| 581 |         rowBytes = blockCount.x * blockSizeInBytes; | 
| 582 |         if (fv == KTX_FORMAT_VERSION_ONE) | 
| 583 |             (void)padRow(&rowBytes); | 
| 584 |         return rowBytes * blockCount.y; | 
| 585 |     } | 
| 586 | } | 
| 587 |  | 
| 588 | /** | 
| 589 |  * @memberof ktxTexture | 
| 590 |  * @~English | 
| 591 |  * @brief Iterate over the levels or faces in a ktxTexture object. | 
| 592 |  * | 
| 593 |  * Blocks of image data are passed to an application-supplied callback | 
| 594 |  * function. This is not a strict per-image iteration. Rather it reflects how | 
| 595 |  * OpenGL needs the images. For most textures the block of data includes all | 
| 596 |  * images of a mip level which implies all layers of an array. However, for | 
| 597 |  * non-array cube map textures the block is a single face of the mip level, | 
| 598 |  * i.e the callback is called once for each face. | 
| 599 |  * | 
| 600 |  * This function works even if @p This->pData == 0 so it can be used to | 
| 601 |  * obtain offsets and sizes for each level by callers who have loaded the data | 
| 602 |  * externally. | 
| 603 |  * | 
| 604 |  * @param[in]     This      pointer to the ktxTexture object of interest. | 
| 605 |  * @param[in,out] iterCb    the address of a callback function which is called | 
| 606 |  *                          with the data for each image block. | 
| 607 |  * @param[in,out] userdata  the address of application-specific data which is | 
| 608 |  *                          passed to the callback along with the image data. | 
| 609 |  * | 
| 610 |  * @return  KTX_SUCCESS on success, other KTX_* enum values on error. The | 
| 611 |  *          following are returned directly by this function. @p iterCb may | 
| 612 |  *          return these for other causes or may return additional errors. | 
| 613 |  * | 
| 614 |  * @exception KTX_FILE_DATA_ERROR   Mip level sizes are increasing not | 
| 615 |  *                                  decreasing | 
| 616 |  * @exception KTX_INVALID_VALUE     @p This is @c NULL or @p iterCb is @c NULL. | 
| 617 |  * | 
| 618 |  */ | 
| 619 | KTX_error_code | 
| 620 | ktxTexture_IterateLevelFaces(ktxTexture* This, PFNKTXITERCB iterCb, | 
| 621 |                              void* userdata) | 
| 622 | { | 
| 623 |     ktx_uint32_t    miplevel; | 
| 624 |     KTX_error_code  result = KTX_SUCCESS; | 
| 625 |  | 
| 626 |     if (This == NULL) | 
| 627 |         return KTX_INVALID_VALUE; | 
| 628 |  | 
| 629 |     if (iterCb == NULL) | 
| 630 |         return KTX_INVALID_VALUE; | 
| 631 |  | 
| 632 |     for (miplevel = 0; miplevel < This->numLevels; ++miplevel) | 
| 633 |     { | 
| 634 |         ktx_uint32_t faceLodSize; | 
| 635 |         ktx_uint32_t face; | 
| 636 |         ktx_uint32_t innerIterations; | 
| 637 |         GLsizei      width, height, depth; | 
| 638 |  | 
| 639 |         /* Array textures have the same number of layers at each mip level. */ | 
| 640 |         width = MAX(1, This->baseWidth  >> miplevel); | 
| 641 |         height = MAX(1, This->baseHeight >> miplevel); | 
| 642 |         depth = MAX(1, This->baseDepth  >> miplevel); | 
| 643 |  | 
| 644 |         faceLodSize = (ktx_uint32_t)ktxTexture_calcFaceLodSize( | 
| 645 |                                                     This, miplevel); | 
| 646 |  | 
| 647 |         /* All array layers are passed in a group because that is how | 
| 648 |          * GL & Vulkan need them. Hence no | 
| 649 |          *    for (layer = 0; layer < This->numLayers) | 
| 650 |          */ | 
| 651 |         if (This->isCubemap && !This->isArray) | 
| 652 |             innerIterations = This->numFaces; | 
| 653 |         else | 
| 654 |             innerIterations = 1; | 
| 655 |         for (face = 0; face < innerIterations; ++face) | 
| 656 |         { | 
| 657 |             /* And all z_slices are also passed as a group hence no | 
| 658 |              *    for (slice = 0; slice < This->depth) | 
| 659 |              */ | 
| 660 |             ktx_size_t offset; | 
| 661 |  | 
| 662 |             ktxTexture_GetImageOffset(This, miplevel, 0, face, &offset); | 
| 663 |             result = iterCb(miplevel, face, | 
| 664 |                             width, height, depth, | 
| 665 |                             faceLodSize, This->pData + offset, userdata); | 
| 666 |  | 
| 667 |             if (result != KTX_SUCCESS) | 
| 668 |                 break; | 
| 669 |         } | 
| 670 |     } | 
| 671 |  | 
| 672 |     return result; | 
| 673 | } | 
| 674 |  | 
| 675 | /** | 
| 676 |  * @internal | 
| 677 |  * @brief  Calculate and apply the padding needed to comply with | 
| 678 |  *         KTX_GL_UNPACK_ALIGNMENT. | 
| 679 |  * | 
| 680 |  * For uncompressed textures, KTX format specifies KTX_GL_UNPACK_ALIGNMENT = 4. | 
| 681 |  * | 
| 682 |  * @param[in,out] rowBytes    pointer to variable containing the packed no. of | 
| 683 |  *                            bytes in a row. The no. of bytes after padding | 
| 684 |  *                            is written into this location. | 
| 685 |  * @return the no. of bytes of padding. | 
| 686 |  */ | 
| 687 | static ktx_uint32_t | 
| 688 | padRow(ktx_uint32_t* rowBytes) | 
| 689 | { | 
| 690 |     ktx_uint32_t rowPadding; | 
| 691 |  | 
| 692 |     assert (rowBytes != NULL); | 
| 693 |  | 
| 694 |     rowPadding = _KTX_PAD_UNPACK_ALIGN_LEN(*rowBytes); | 
| 695 |     *rowBytes += rowPadding; | 
| 696 |     return rowPadding; | 
| 697 | } | 
| 698 |  | 
| 699 | /** | 
| 700 |  * @memberof ktxTexture @private | 
| 701 |  * @~English | 
| 702 |  * @brief Calculate the size of an array layer at the specified mip level. | 
| 703 |  * | 
| 704 |  * The size of a layer is the size of an image * either the number of faces | 
| 705 |  * or the number of depth slices. This is the size of a layer as needed to | 
| 706 |  * find the offset within the array of images of a level and layer so the size | 
| 707 |  * reflects any @c cubePadding. | 
| 708 |  * | 
| 709 |  * @param[in]  This     pointer to the ktxTexture object of interest. | 
| 710 |  * @param[in] level     level whose layer size to return. | 
| 711 |  * | 
| 712 |  * @return the layer size in bytes. | 
| 713 |  */ | 
| 714 | ktx_size_t | 
| 715 | ktxTexture_layerSize(ktxTexture* This, ktx_uint32_t level, | 
| 716 |                     ktxFormatVersionEnum fv) | 
| 717 | { | 
| 718 |     /* | 
| 719 |      * As there are no 3D cubemaps, the image's z block count will always be | 
| 720 |      * 1 for cubemaps and numFaces will always be 1 for 3D textures so the | 
| 721 |      * multiply is safe. 3D cubemaps, if they existed, would require | 
| 722 |      * imageSize * (blockCount.z + This->numFaces); | 
| 723 |      */ | 
| 724 |     DECLARE_PROTECTED(ktxTexture); | 
| 725 |     ktx_uint32_t blockCountZ; | 
| 726 |     ktx_size_t imageSize, layerSize; | 
| 727 |  | 
| 728 |     assert (This != NULL); | 
| 729 |  | 
| 730 |     blockCountZ = MAX(1, (This->baseDepth / prtctd->_formatSize.blockDepth)  >> level); | 
| 731 |     imageSize = ktxTexture_calcImageSize(This, level, fv); | 
| 732 |     layerSize = imageSize * blockCountZ; | 
| 733 |     if (fv == KTX_FORMAT_VERSION_ONE && KTX_GL_UNPACK_ALIGNMENT != 4) { | 
| 734 |         if (This->isCubemap && !This->isArray) { | 
| 735 |             /* cubePadding. NOTE: this adds padding after the last face too. */ | 
| 736 |             layerSize += _KTX_PAD4(layerSize); | 
| 737 |         } | 
| 738 |     } | 
| 739 |     return layerSize * This->numFaces; | 
| 740 | } | 
| 741 |  | 
| 742 | /** | 
| 743 |  * @memberof ktxTexture @private | 
| 744 |  * @~English | 
| 745 |  * @brief Calculate the size of the specified mip level. | 
| 746 |  * | 
| 747 |  * The size of a level is the size of a layer * the number of layers. | 
| 748 |  * | 
| 749 |  * @param[in]  This     pointer to the ktxTexture object of interest. | 
| 750 |  * @param[in] level     level whose layer size to return. | 
| 751 |  * | 
| 752 |  * @return the level size in bytes. | 
| 753 |  */ | 
| 754 | ktx_size_t | 
| 755 | ktxTexture_calcLevelSize(ktxTexture* This, ktx_uint32_t level, | 
| 756 |                          ktxFormatVersionEnum fv) | 
| 757 | { | 
| 758 |     assert (This != NULL); | 
| 759 |     assert (level < This->numLevels); | 
| 760 |     return ktxTexture_layerSize(This, level, fv) * This->numLayers; | 
| 761 | } | 
| 762 |  | 
| 763 | /** | 
| 764 |  * @memberof ktxTexture @private | 
| 765 |  * @~English | 
| 766 |  * @brief Calculate the faceLodSize of the specified mip level. | 
| 767 |  * | 
| 768 |  * The faceLodSize of a level for most textures is the size of a level. For | 
| 769 |  * non-array cube map textures is the size of a face. This is the size that | 
| 770 |  * must be provided to OpenGL when uploading textures. Faces get uploaded 1 | 
| 771 |  * at a time while all layers of an array or all slices of a 3D texture are | 
| 772 |  * uploaded together. | 
| 773 |  * | 
| 774 |  * @param[in]  This     pointer to the ktxTexture object of interest. | 
| 775 |  * @param[in] level     level whose layer size to return. | 
| 776 |  * | 
| 777 |  * @return the faceLodSize size in bytes. | 
| 778 |  */ | 
| 779 | ktx_size_t | 
| 780 | ktxTexture_doCalcFaceLodSize(ktxTexture* This, ktx_uint32_t level, | 
| 781 |                              ktxFormatVersionEnum fv) | 
| 782 | { | 
| 783 |     /* | 
| 784 |      * For non-array cubemaps this is the size of a face. For everything | 
| 785 |      * else it is the size of the level. | 
| 786 |      */ | 
| 787 |     if (This->isCubemap && !This->isArray) | 
| 788 |         return ktxTexture_calcImageSize(This, level, fv); | 
| 789 |     else | 
| 790 |         return ktxTexture_calcLevelSize(This, level, fv); | 
| 791 | } | 
| 792 |  | 
| 793 |  | 
| 794 | /** | 
| 795 |  * @memberof ktxTexture @private | 
| 796 |  * @~English | 
| 797 |  * @brief Return the number of bytes needed to store all the image data for | 
| 798 |  *        a ktxTexture. | 
| 799 |  * | 
| 800 |  * The caclulated size does not include space for storing the @c imageSize | 
| 801 |  * fields of each mip level. | 
| 802 |  * | 
| 803 |  * @param[in]     This  pointer to the ktxTexture object of interest. | 
| 804 |  * @param[in]     fv    enum specifying format version for which to calculate | 
| 805 |  *                      image size. | 
| 806 |  * | 
| 807 |  * @return the data size in bytes. | 
| 808 |  */ | 
| 809 | ktx_size_t | 
| 810 | ktxTexture_calcDataSizeTexture(ktxTexture* This) | 
| 811 | { | 
| 812 |     assert (This != NULL); | 
| 813 |     return ktxTexture_calcDataSizeLevels(This, This->numLevels); | 
| 814 | } | 
| 815 |  | 
| 816 | /** | 
| 817 |  * @memberof ktxTexture @private | 
| 818 |  * @~English | 
| 819 |  * @brief Get information about rows of an uncompresssed texture image at a | 
| 820 |  *        specified level. | 
| 821 |  * | 
| 822 |  * For an image at @p level of a ktxTexture provide the number of rows, the | 
| 823 |  * packed (unpadded) number of bytes in a row and the padding necessary to | 
| 824 |  * comply with KTX_GL_UNPACK_ALIGNMENT. | 
| 825 |  * | 
| 826 |  * @param[in]     This     pointer to the ktxTexture object of interest. | 
| 827 |  * @param[in]     level    level of interest. | 
| 828 |  * @param[in,out] numRows  pointer to location to store the number of rows. | 
| 829 |  * @param[in,out] pRowLengthBytes pointer to location to store number of bytes | 
| 830 |  *                                in a row. | 
| 831 |  * @param[in.out] pRowPadding pointer to location to store the number of bytes | 
| 832 |  *                            of padding. | 
| 833 |  */ | 
| 834 | void | 
| 835 | ktxTexture_rowInfo(ktxTexture* This, ktx_uint32_t level, | 
| 836 |                    ktx_uint32_t* numRows, ktx_uint32_t* pRowLengthBytes, | 
| 837 |                    ktx_uint32_t* pRowPadding) | 
| 838 | { | 
| 839 |     DECLARE_PROTECTED(ktxTexture); | 
| 840 |     struct blockCount { | 
| 841 |         ktx_uint32_t x; | 
| 842 |     } blockCount; | 
| 843 |  | 
| 844 |     assert (This != NULL); | 
| 845 |  | 
| 846 |     assert(!This->isCompressed); | 
| 847 |     assert(prtctd->_formatSize.blockWidth == 1U | 
| 848 |            && prtctd->_formatSize.blockHeight == 1U | 
| 849 |            && prtctd->_formatSize.blockDepth == 1U); | 
| 850 |  | 
| 851 |     blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth)  >> level); | 
| 852 |     *numRows = MAX(1, (This->baseHeight / prtctd->_formatSize.blockHeight)  >> level); | 
| 853 |  | 
| 854 |     *pRowLengthBytes = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8; | 
| 855 |     *pRowPadding = padRow(pRowLengthBytes); | 
| 856 | } | 
| 857 |  | 
| 858 | /** | 
| 859 |  * @memberof ktxTexture | 
| 860 |  * @~English | 
| 861 |  * @brief Return pitch betweeb rows of a texture image level in bytes. | 
| 862 |  * | 
| 863 |  * For uncompressed textures the pitch is the number of bytes between | 
| 864 |  * rows of texels. For compressed textures it is the number of bytes | 
| 865 |  * between rows of blocks. The value is padded to GL_UNPACK_ALIGNMENT, | 
| 866 |  * if necessary. For all currently known compressed formats padding | 
| 867 |  * will not be necessary. | 
| 868 |  * | 
| 869 |  * @param[in]     This     pointer to the ktxTexture object of interest. | 
| 870 |  * @param[in]     level    level of interest. | 
| 871 |  * | 
| 872 |  * @return  the row pitch in bytes. | 
| 873 |  */ | 
| 874 |  ktx_uint32_t | 
| 875 |  ktxTexture_GetRowPitch(ktxTexture* This, ktx_uint32_t level) | 
| 876 |  { | 
| 877 |     DECLARE_PROTECTED(ktxTexture) | 
| 878 |     struct blockCount { | 
| 879 |         ktx_uint32_t x; | 
| 880 |     } blockCount; | 
| 881 |     ktx_uint32_t pitch; | 
| 882 |  | 
| 883 |     blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth)  >> level); | 
| 884 |     pitch = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8; | 
| 885 |     (void)padRow(&pitch); | 
| 886 |  | 
| 887 |     return pitch; | 
| 888 |  } | 
| 889 |  | 
| 890 | /** | 
| 891 |  * @memberof ktxTexture @private | 
| 892 |  * @~English | 
| 893 |  * @brief Query if a ktxTexture has an active stream. | 
| 894 |  * | 
| 895 |  * Tests if a ktxTexture has unread image data. The internal stream is closed | 
| 896 |  * once all the images have been read. | 
| 897 |  * | 
| 898 |  * @param[in]     This     pointer to the ktxTexture object of interest. | 
| 899 |  * | 
| 900 |  * @return KTX_TRUE if there is an active stream, KTX_FALSE otherwise. | 
| 901 |  */ | 
| 902 | ktx_bool_t | 
| 903 | ktxTexture_isActiveStream(ktxTexture* This) | 
| 904 | { | 
| 905 |     assert(This != NULL); | 
| 906 |     ktxStream* stream = ktxTexture_getStream(This); | 
| 907 |     return stream->data.file != NULL; | 
| 908 | } | 
| 909 |  | 
| 910 | /** @} */ | 
| 911 |  | 
| 912 |  |