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
41ktx_size_t ktxTexture_GetDataSize(ktxTexture* This);
42
43static 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 */
83KTX_error_code
84ktxTexture_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 */
190KTX_error_code
191ktxTexture_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 */
225void
226ktxTexture_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
248typedef enum { KTX1, KTX2 } ktxFileType_;
249typedef union {
250 KTX_header ktx;
251 KTX_header2 ktx2;
252} ktxHeaderUnion_;
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 */
265static KTX_error_code
266ktxDetermineFileType_(ktxStream* pStream, ktxFileType_* pFileType,
267 ktxHeaderUnion_* pHeader)
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 */
313KTX_error_code
314ktxTexture_CreateFromStream(ktxStream* pStream,
315 ktxTextureCreateFlags createFlags,
316 ktxTexture** newTex)
317{
318 ktxHeaderUnion_ header;
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 */
364KTX_error_code
365ktxTexture_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 */
412KTX_error_code
413ktxTexture_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 */
465KTX_error_code
466ktxTexture_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 */
490ktx_uint8_t*
491ktxTexture_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 */
506ktx_size_t
507ktxTexture_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 */
524ktx_uint32_t
525ktxTexture_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 */
548ktx_size_t
549ktxTexture_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 */
619KTX_error_code
620ktxTexture_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 */
687static ktx_uint32_t
688padRow(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 */
714ktx_size_t
715ktxTexture_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 */
754ktx_size_t
755ktxTexture_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 */
779ktx_size_t
780ktxTexture_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 */
809ktx_size_t
810ktxTexture_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 */
834void
835ktxTexture_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 */
902ktx_bool_t
903ktxTexture_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