1/* -*- tab-width: 4; -*- */
2/* vi: set sw=2 ts=4 expandtab: */
3
4/*
5 * Copyright 2019-2020 The Khronos Group Inc.
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9/**
10 * @internal
11 * @file texture2.c
12 * @~English
13 *
14 * @brief ktxTexture1 implementation. Support for KTX format.
15 *
16 * @author Mark Callow, www.edgewise-consulting.com
17 */
18
19#if defined(_WIN32)
20#define _CRT_SECURE_NO_WARNINGS
21#endif
22
23#include <stdlib.h>
24#include <string.h>
25
26#include "dfdutils/dfd.h"
27#include "ktx.h"
28#include "ktxint.h"
29#include "filestream.h"
30#include "memstream.h"
31#include "texture1.h"
32#include "unused.h"
33#include "gl_format.h"
34
35typedef struct ktxTexture1_private {
36 ktx_bool_t _needSwap;
37} ktxTexture1_private;
38
39struct ktxTexture_vtbl ktxTexture1_vtbl;
40struct ktxTexture_vtblInt ktxTexture1_vtblInt;
41
42static KTX_error_code
43ktxTexture1_constructCommon(ktxTexture1* This)
44{
45 assert(This != NULL);
46
47 This->classId = ktxTexture1_c;
48 This->vtbl = &ktxTexture1_vtbl;
49 This->_protected->_vtbl = ktxTexture1_vtblInt;
50 This->_private = (ktxTexture1_private*)malloc(sizeof(ktxTexture1_private));
51 if (This->_private == NULL) {
52 return KTX_OUT_OF_MEMORY;
53 }
54 memset(This->_private, 0, sizeof(*This->_private));
55
56 return KTX_SUCCESS;
57}
58
59/**
60 * @memberof ktxTexture1 @private
61 * @copydoc ktxTexture2_construct
62 */
63static KTX_error_code
64ktxTexture1_construct(ktxTexture1* This, ktxTextureCreateInfo* createInfo,
65 ktxTextureCreateStorageEnum storageAllocation)
66{
67 ktxTexture_protected* prtctd;
68 ktxFormatSize formatSize;
69 GLuint typeSize;
70 GLenum glFormat;
71 KTX_error_code result;
72
73 memset(This, 0, sizeof(*This));
74
75 This->glInternalformat = createInfo->glInternalformat;
76 glGetFormatSize(This->glInternalformat, &formatSize);
77 if (formatSize.blockSizeInBits == 0) {
78 // Most likely a deprecated legacy format.
79 return KTX_UNSUPPORTED_TEXTURE_TYPE;
80 }
81 glFormat= glGetFormatFromInternalFormat(createInfo->glInternalformat);
82 if (glFormat == GL_INVALID_VALUE) {
83 return KTX_INVALID_VALUE;
84 }
85 result = ktxTexture_construct(ktxTexture(This), createInfo, &formatSize);
86 if (result != KTX_SUCCESS)
87 return result;
88
89 result = ktxTexture1_constructCommon(This);
90 if (result != KTX_SUCCESS)
91 return result;
92 prtctd = This->_protected;
93
94 This->isCompressed
95 = (formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);
96 if (This->isCompressed) {
97 This->glFormat = 0;
98 This->glBaseInternalformat = glFormat;
99 This->glType = 0;
100 prtctd->_typeSize = 1;
101 } else {
102 This->glBaseInternalformat = This->glFormat = glFormat;
103 This->glType
104 = glGetTypeFromInternalFormat(createInfo->glInternalformat);
105 if (This->glType == GL_INVALID_VALUE) {
106 result = KTX_INVALID_VALUE;
107 goto cleanup;
108 }
109 typeSize = glGetTypeSizeFromType(This->glType);
110 assert(typeSize != GL_INVALID_VALUE);
111
112 /* Do some sanity checking */
113 if (typeSize != 1 &&
114 typeSize != 2 &&
115 typeSize != 4)
116 {
117 /* Only 8, 16, and 32-bit types are supported for byte-swapping.
118 * See UNPACK_SWAP_BYTES & table 8.4 in the OpenGL 4.4 spec.
119 */
120 result = KTX_INVALID_VALUE;
121 goto cleanup;
122 }
123 prtctd->_typeSize = typeSize;
124 }
125
126 if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) {
127 This->dataSize
128 = ktxTexture_calcDataSizeTexture(ktxTexture(This));
129 This->pData = malloc(This->dataSize);
130 if (This->pData == NULL) {
131 result = KTX_OUT_OF_MEMORY;
132 goto cleanup;
133 }
134 }
135 return result;
136
137cleanup:
138 ktxTexture1_destruct(This);
139 ktxTexture_destruct(ktxTexture(This));
140 return result;
141}
142
143/**
144 * @memberof ktxTexture1 @private
145 * @brief Construct a ktxTexture1 from a ktxStream reading from a KTX source.
146 *
147 * The KTX header, that must have been read prior to calling this, is passed
148 * to the function.
149 *
150 * The stream object is copied into the constructed ktxTexture1.
151 *
152 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
153 * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
154 * will minimize memory usage by allowing, for example, loading the images
155 * directly from the source into a Vulkan staging buffer.
156 *
157 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
158 * provided solely to enable implementation of the @e libktx v1 API on top of
159 * ktxTexture1.
160 *
161 * @param[in] This pointer to a ktxTexture1-sized block of memory to
162 * initialize.
163 * @param[in] pStream pointer to the stream to read.
164 * @param[in] pHeader pointer to a KTX header that has already been read from
165 * the stream.
166 * @param[in] createFlags bitmask requesting specific actions during creation.
167 *
168 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
169 *
170 * @exception KTX_FILE_DATA_ERROR
171 * Source data is inconsistent with the KTX
172 * specification.
173 * @exception KTX_FILE_READ_ERROR
174 * An error occurred while reading the source.
175 * @exception KTX_FILE_UNEXPECTED_EOF
176 * Not enough data in the source.
177 * @exception KTX_OUT_OF_MEMORY Not enough memory to load either the images or
178 * the key-value data.
179 * @exception KTX_UNKNOWN_FILE_FORMAT
180 * The source is not in KTX format.
181 * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
182 * The source describes a texture type not
183 * supported by OpenGL or Vulkan, e.g, a 3D array.
184 */
185KTX_error_code
186ktxTexture1_constructFromStreamAndHeader(ktxTexture1* This, ktxStream* pStream,
187 KTX_header* pHeader,
188 ktxTextureCreateFlags createFlags)
189{
190 ktxTexture1_private* private;
191 KTX_error_code result;
192 KTX_supplemental_info suppInfo;
193 ktxStream* stream;
194 ktx_off_t pos;
195 ktx_size_t size;
196 ktxFormatSize formatSize;
197
198 assert(pHeader != NULL && pStream != NULL);
199
200 memset(This, 0, sizeof(*This));
201 result = ktxTexture_constructFromStream(ktxTexture(This), pStream, createFlags);
202 if (result != KTX_SUCCESS)
203 return result;
204 result = ktxTexture1_constructCommon(This);
205 if (result != KTX_SUCCESS) {
206 ktxTexture_destruct(ktxTexture(This));
207 return result;
208 }
209
210 private = This->_private;
211 stream = ktxTexture1_getStream(This);
212
213 result = ktxCheckHeader1_(pHeader, &suppInfo);
214 if (result != KTX_SUCCESS)
215 goto cleanup;
216
217 /*
218 * Initialize from pHeader info.
219 */
220 This->glFormat = pHeader->glFormat;
221 This->glInternalformat = pHeader->glInternalformat;
222 This->glType = pHeader->glType;
223 glGetFormatSize(This->glInternalformat, &formatSize);
224 if (formatSize.blockSizeInBits == 0) {
225 // Most likely a deprecated legacy format.
226 result = KTX_UNSUPPORTED_TEXTURE_TYPE;
227 goto cleanup;
228 }
229 This->_protected->_formatSize = formatSize;
230 This->glBaseInternalformat = pHeader->glBaseInternalformat;
231 // Can these be done by a ktxTexture_constructFromStream?
232 This->numDimensions = suppInfo.textureDimension;
233 This->baseWidth = pHeader->pixelWidth;
234 assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4);
235 switch (suppInfo.textureDimension) {
236 case 1:
237 This->baseHeight = This->baseDepth = 1;
238 break;
239 case 2:
240 This->baseHeight = pHeader->pixelHeight;
241 This->baseDepth = 1;
242 break;
243 case 3:
244 This->baseHeight = pHeader->pixelHeight;
245 This->baseDepth = pHeader->pixelDepth;
246 break;
247 }
248 if (pHeader->numberOfArrayElements > 0) {
249 This->numLayers = pHeader->numberOfArrayElements;
250 This->isArray = KTX_TRUE;
251 } else {
252 This->numLayers = 1;
253 This->isArray = KTX_FALSE;
254 }
255 This->numFaces = pHeader->numberOfFaces;
256 if (pHeader->numberOfFaces == 6)
257 This->isCubemap = KTX_TRUE;
258 else
259 This->isCubemap = KTX_FALSE;
260 This->numLevels = pHeader->numberOfMipLevels;
261 This->isCompressed = suppInfo.compressed;
262 This->generateMipmaps = suppInfo.generateMipmaps;
263 if (pHeader->endianness == KTX_ENDIAN_REF_REV)
264 private->_needSwap = KTX_TRUE;
265 This->_protected->_typeSize = pHeader->glTypeSize;
266
267 /*
268 * Make an empty hash list.
269 */
270 ktxHashList_Construct(&This->kvDataHead);
271 /*
272 * Load KVData.
273 */
274 if (pHeader->bytesOfKeyValueData > 0) {
275 if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) {
276 ktx_uint32_t kvdLen = pHeader->bytesOfKeyValueData;
277 ktx_uint8_t* pKvd;
278
279 pKvd = malloc(kvdLen);
280 if (pKvd == NULL) {
281 result = KTX_OUT_OF_MEMORY;
282 goto cleanup;
283 }
284
285 result = stream->read(stream, pKvd, kvdLen);
286 if (result != KTX_SUCCESS)
287 goto cleanup;
288
289 if (private->_needSwap) {
290 /* Swap the counts inside the key & value data. */
291 ktx_uint8_t* src = pKvd;
292 ktx_uint8_t* end = pKvd + kvdLen;
293 while (src < end) {
294 ktx_uint32_t* pKeyAndValueByteSize = (ktx_uint32_t*)src;
295 _ktxSwapEndian32(pKeyAndValueByteSize, 1);
296 src += _KTX_PAD4(*pKeyAndValueByteSize);
297 }
298 }
299
300 if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) {
301 char* orientation;
302 ktx_uint32_t orientationLen;
303
304 result = ktxHashList_Deserialize(&This->kvDataHead,
305 kvdLen, pKvd);
306 free(pKvd);
307 if (result != KTX_SUCCESS) {
308 goto cleanup;
309 }
310
311 result = ktxHashList_FindValue(&This->kvDataHead,
312 KTX_ORIENTATION_KEY,
313 &orientationLen,
314 (void**)&orientation);
315 assert(result != KTX_INVALID_VALUE);
316 if (result == KTX_SUCCESS) {
317 ktx_uint32_t count;
318 char orient[4] = {0, 0, 0, 0};
319
320 count = sscanf(orientation, KTX_ORIENTATION3_FMT,
321 &orient[0],
322 &orient[1],
323 &orient[2]);
324
325 if (count > This->numDimensions) {
326 // KTX 1 is less strict than KTX2 so there is a chance
327 // of having more dimensions than needed.
328 count = This->numDimensions;
329 }
330 switch (This->numDimensions) {
331 case 3:
332 This->orientation.z = orient[2];
333 FALLTHROUGH;
334 case 2:
335 This->orientation.y = orient[1];
336 FALLTHROUGH;
337 case 1:
338 This->orientation.x = orient[0];
339 }
340 }
341 } else {
342 This->kvDataLen = kvdLen;
343 This->kvData = pKvd;
344 }
345 } else {
346 stream->skip(stream, pHeader->bytesOfKeyValueData);
347 }
348 }
349
350 /*
351 * Get the size of the image data.
352 */
353 result = stream->getsize(stream, &size);
354 if (result != KTX_SUCCESS)
355 goto cleanup;
356
357 result = stream->getpos(stream, &pos);
358 if (result != KTX_SUCCESS)
359 goto cleanup;
360
361 /* Remove space for faceLodSize fields */
362 This->dataSize = size - pos - This->numLevels * sizeof(ktx_uint32_t);
363
364 /*
365 * Load the images, if requested.
366 */
367 if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) {
368 result = ktxTexture1_LoadImageData(This, NULL, 0);
369 }
370 if (result == KTX_SUCCESS)
371 return result;
372
373cleanup:
374 ktxTexture1_destruct(This);
375 return result;
376}
377
378/**
379 * @memberof ktxTexture1 @private
380 * @brief Construct a ktxTexture1 from a ktxStream reading from a KTX source.
381 *
382 * The stream object is copied into the constructed ktxTexture1.
383 *
384 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
385 * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
386 * will minimize memory usage by allowing, for example, loading the images
387 * directly from the source into a Vulkan staging buffer.
388 *
389 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
390 * provided solely to enable implementation of the @e libktx v1 API on top of
391 * ktxTexture1.
392 *
393 * @param[in] This pointer to a ktxTexture1-sized block of memory to
394 * initialize.
395 * @param[in] pStream pointer to the stream to read.
396 * @param[in] createFlags bitmask requesting specific actions during creation.
397 *
398 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
399 *
400 * @exception KTX_FILE_READ_ERROR
401 * An error occurred while reading the source.
402 *
403 * For other exceptions see ktxTexture1_constructFromStreamAndHeader().
404 */
405static KTX_error_code
406ktxTexture1_constructFromStream(ktxTexture1* This, ktxStream* pStream,
407 ktxTextureCreateFlags createFlags)
408{
409 KTX_header header;
410 KTX_error_code result;
411
412 // Read header.
413 result = pStream->read(pStream, &header, KTX_HEADER_SIZE);
414 if (result != KTX_SUCCESS)
415 return result;
416
417 return ktxTexture1_constructFromStreamAndHeader(This, pStream,
418 &header, createFlags);
419}
420
421/**
422 * @memberof ktxTexture1 @private
423 * @brief Construct a ktxTexture1 from a stdio stream reading from a KTX source.
424 *
425 * See ktxTextureInt_constructFromStream for details.
426 *
427 * @note Do not close the stdio stream until you are finished with the texture
428 * object.
429 *
430 * @param[in] This pointer to a ktxTextureInt-sized block of memory to
431 * initialize.
432 * @param[in] stdioStream a stdio FILE pointer opened on the source.
433 * @param[in] createFlags bitmask requesting specific actions during creation.
434 *
435 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
436 *
437 * @exception KTX_INVALID_VALUE Either @p stdiostream or @p This is null.
438 *
439 * For other exceptions, see ktxTexture_constructFromStream().
440 */
441static KTX_error_code
442ktxTexture1_constructFromStdioStream(ktxTexture1* This, FILE* stdioStream,
443 ktxTextureCreateFlags createFlags)
444{
445 ktxStream stream;
446 KTX_error_code result;
447
448 if (stdioStream == NULL || This == NULL)
449 return KTX_INVALID_VALUE;
450
451 result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);
452 if (result == KTX_SUCCESS)
453 result = ktxTexture1_constructFromStream(This, &stream, createFlags);
454 return result;
455}
456
457/**
458 * @memberof ktxTexture1 @private
459 * @brief Construct a ktxTexture1 from a named KTX file.
460 *
461 * See ktxTextureInt_constructFromStream for details.
462 *
463 * @param[in] This pointer to a ktxTextureInt-sized block of memory to
464 * initialize.
465 * @param[in] filename pointer to a char array containing the file name.
466 * @param[in] createFlags bitmask requesting specific actions during creation.
467 *
468 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
469 *
470 * @exception KTX_FILE_OPEN_FAILED The file could not be opened.
471 * @exception KTX_INVALID_VALUE @p filename is @c NULL.
472 *
473 * For other exceptions, see ktxTexture_constructFromStream().
474 */
475static KTX_error_code
476ktxTexture1_constructFromNamedFile(ktxTexture1* This,
477 const char* const filename,
478 ktxTextureCreateFlags createFlags)
479{
480 FILE* file;
481 ktxStream stream;
482 KTX_error_code result;
483
484 if (This == NULL || filename == NULL)
485 return KTX_INVALID_VALUE;
486
487 file = fopen(filename, "rb");
488 if (!file)
489 return KTX_FILE_OPEN_FAILED;
490
491 result = ktxFileStream_construct(&stream, file, KTX_TRUE);
492 if (result == KTX_SUCCESS)
493 result = ktxTexture1_constructFromStream(This, &stream, createFlags);
494
495 return result;
496}
497
498/**
499 * @memberof ktxTexture1 @private
500 * @brief Construct a ktxTexture1 from KTX-formatted data in memory.
501 *
502 * See ktxTextureInt_constructFromStream for details.
503 *
504 * @param[in] This pointer to a ktxTextureInt-sized block of memory to
505 * initialize.
506 * @param[in] bytes pointer to the memory containing the serialized KTX data.
507 * @param[in] size length of the KTX data in bytes.
508 * @param[in] createFlags bitmask requesting specific actions during creation.
509 *
510 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
511 *
512 * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
513 *
514 * For other exceptions, see ktxTexture_constructFromStream().
515 */
516static KTX_error_code
517ktxTexture1_constructFromMemory(ktxTexture1* This,
518 const ktx_uint8_t* bytes, ktx_size_t size,
519 ktxTextureCreateFlags createFlags)
520{
521 ktxStream stream;
522 KTX_error_code result;
523
524 if (bytes == NULL || size == 0)
525 return KTX_INVALID_VALUE;
526
527 result = ktxMemStream_construct_ro(&stream, bytes, size);
528 if (result == KTX_SUCCESS)
529 result = ktxTexture1_constructFromStream(This, &stream, createFlags);
530
531 return result;
532}
533
534void
535ktxTexture1_destruct(ktxTexture1* This)
536{
537 if (This->_private) free(This->_private);
538 ktxTexture_destruct(ktxTexture(This));
539}
540
541/**
542 * @defgroup reader Reader
543 * @brief Read KTX-formatted data.
544 * @{
545 */
546
547/**
548 * @memberof ktxTexture1
549 * @ingroup writer
550 * @brief Create a new empty ktxTexture1.
551 *
552 * The address of the newly created ktxTexture1 is written to the location
553 * pointed at by @p newTex.
554 *
555 * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with
556 * information describing the texture.
557 * @param[in] storageAllocation
558 * enum indicating whether or not to allocate storage
559 * for the texture images.
560 * @param[in,out] newTex pointer to a location in which store the address of
561 * the newly created texture.
562 *
563 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
564 *
565 * @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a
566 * valid OpenGL internal format value.
567 * @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2
568 * or 3.
569 * @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in
570 * @p createInfo is 0.
571 * @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.
572 * @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.
573 * @exception KTX_INVALID_OPERATION
574 * The <tt>base{Width,Height,Depth}</tt> specified
575 * in @p createInfo are inconsistent with
576 * @c numDimensions.
577 * @exception KTX_INVALID_OPERATION
578 * @p createInfo is requesting a 3D array or
579 * 3D cubemap texture.
580 * @exception KTX_INVALID_OPERATION
581 * @p createInfo is requesting a cubemap with
582 * non-square or non-2D images.
583 * @exception KTX_INVALID_OPERATION
584 * @p createInfo is requesting more mip levels
585 * than needed for the specified
586 * <tt>base{Width,Height,Depth}</tt>.
587 * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture's images.
588 */
589KTX_error_code
590ktxTexture1_Create(ktxTextureCreateInfo* createInfo,
591 ktxTextureCreateStorageEnum storageAllocation,
592 ktxTexture1** newTex)
593{
594 KTX_error_code result;
595
596 if (newTex == NULL)
597 return KTX_INVALID_VALUE;
598
599 ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
600 if (tex == NULL)
601 return KTX_OUT_OF_MEMORY;
602
603 result = ktxTexture1_construct(tex, createInfo, storageAllocation);
604 if (result != KTX_SUCCESS) {
605 free(tex);
606 } else {
607 *newTex = tex;
608 }
609 return result;
610}
611
612/**
613 * @memberof ktxTexture1
614 * @~English
615 * @brief Create a ktxTexture1 from a stdio stream reading from a KTX source.
616 *
617 * The address of a newly created ktxTexture1 reflecting the contents of the
618 * stdio stream is written to the location pointed at by @p newTex.
619 *
620 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
621 * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
622 * will minimize memory usage by allowing, for example, loading the images
623 * directly from the source into a Vulkan staging buffer.
624 *
625 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
626 * provided solely to enable implementation of the @e libktx v1 API on top of
627 * ktxTexture1.
628 *
629 * @param[in] stdioStream stdio FILE pointer created from the desired file.
630 * @param[in] createFlags bitmask requesting specific actions during creation.
631 * @param[in,out] newTex pointer to a location in which store the address of
632 * the newly created texture.
633 *
634 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
635 *
636 * @exception KTX_INVALID_VALUE @p newTex is @c NULL.
637 * @exception KTX_FILE_DATA_ERROR
638 * Source data is inconsistent with the KTX
639 * specification.
640 * @exception KTX_FILE_READ_ERROR
641 * An error occurred while reading the source.
642 * @exception KTX_FILE_UNEXPECTED_EOF
643 * Not enough data in the source.
644 * @exception KTX_OUT_OF_MEMORY Not enough memory to create the texture object,
645 * load the images or load the key-value data.
646 * @exception KTX_UNKNOWN_FILE_FORMAT
647 * The source is not in KTX format.
648 * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
649 * The source describes a texture type not
650 * supported by OpenGL or Vulkan, e.g, a 3D array.
651 */
652KTX_error_code
653ktxTexture1_CreateFromStdioStream(FILE* stdioStream,
654 ktxTextureCreateFlags createFlags,
655 ktxTexture1** newTex)
656{
657 KTX_error_code result;
658 if (newTex == NULL)
659 return KTX_INVALID_VALUE;
660
661 ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
662 if (tex == NULL)
663 return KTX_OUT_OF_MEMORY;
664
665 result = ktxTexture1_constructFromStdioStream(tex, stdioStream,
666 createFlags);
667 if (result == KTX_SUCCESS)
668 *newTex = (ktxTexture1*)tex;
669 else {
670 free(tex);
671 *newTex = NULL;
672 }
673 return result;
674}
675
676/*
677 * @memberof ktxTexture1
678 * @~English
679 * @brief Create a ktxTexture1 from a named KTX file.
680 *
681 * The address of a newly created ktxTexture1 reflecting the contents of the
682 * file is written to the location pointed at by @p newTex.
683 *
684 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
685 * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
686 * will minimize memory usage by allowing, for example, loading the images
687 * directly from the source into a Vulkan staging buffer.
688 *
689 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
690 * provided solely to enable implementation of the @e libktx v1 API on top of
691 * ktxTexture1.
692 *
693 * @param[in] filename pointer to a char array containing the file name.
694 * @param[in] createFlags bitmask requesting specific actions during creation.
695 * @param[in,out] newTex pointer to a location in which store the address of
696 * the newly created texture.
697 *
698 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
699 *
700 * @exception KTX_FILE_OPEN_FAILED The file could not be opened.
701 * @exception KTX_INVALID_VALUE @p filename is @c NULL.
702 *
703 * For other exceptions, see ktxTexture_CreateFromStdioStream().
704 */
705KTX_error_code
706ktxTexture1_CreateFromNamedFile(const char* const filename,
707 ktxTextureCreateFlags createFlags,
708 ktxTexture1** newTex)
709{
710 KTX_error_code result;
711
712 if (newTex == NULL)
713 return KTX_INVALID_VALUE;
714
715 ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
716 if (tex == NULL)
717 return KTX_OUT_OF_MEMORY;
718
719 result = ktxTexture1_constructFromNamedFile(tex, filename, createFlags);
720 if (result == KTX_SUCCESS)
721 *newTex = (ktxTexture1*)tex;
722 else {
723 free(tex);
724 *newTex = NULL;
725 }
726 return result;
727}
728
729/**
730 * @memberof ktxTexture1
731 * @~English
732 * @brief Create a ktxTexture1 from KTX-formatted data in memory.
733 *
734 * The address of a newly created ktxTexture1 reflecting the contents of the
735 * serialized KTX data is written to the location pointed at by @p newTex.
736 *
737 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
738 * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
739 * will minimize memory usage by allowing, for example, loading the images
740 * directly from the source into a Vulkan staging buffer.
741 *
742 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
743 * provided solely to enable implementation of the @e libktx v1 API on top of
744 * ktxTexture1.
745 *
746 * @param[in] bytes pointer to the memory containing the serialized KTX data.
747 * @param[in] size length of the KTX data in bytes.
748 * @param[in] createFlags bitmask requesting specific actions during creation.
749 * @param[in,out] newTex pointer to a location in which store the address of
750 * the newly created texture.
751 *
752 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
753 *
754 * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
755 *
756 * For other exceptions, see ktxTexture_CreateFromStdioStream().
757 */
758KTX_error_code
759ktxTexture1_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
760 ktxTextureCreateFlags createFlags,
761 ktxTexture1** newTex)
762{
763 KTX_error_code result;
764 if (newTex == NULL)
765 return KTX_INVALID_VALUE;
766
767 ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
768 if (tex == NULL)
769 return KTX_OUT_OF_MEMORY;
770
771 result = ktxTexture1_constructFromMemory(tex, bytes, size,
772 createFlags);
773 if (result == KTX_SUCCESS)
774 *newTex = (ktxTexture1*)tex;
775 else {
776 free(tex);
777 *newTex = NULL;
778 }
779 return result;
780}
781
782/**
783 * @memberof ktxTexture1
784 * @~English
785 * @brief Create a ktxTexture1 from KTX-formatted data from a `ktxStream`.
786 *
787 * The address of a newly created ktxTexture1 reflecting the contents of the
788 * serialized KTX data is written to the location pointed at by @p newTex.
789 *
790 * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
791 * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
792 * will minimize memory usage by allowing, for example, loading the images
793 * directly from the source into a Vulkan staging buffer.
794 *
795 * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
796 * provided solely to enable implementation of the @e libktx v1 API on top of
797 * ktxTexture1.
798 *
799 * @param[in] stream pointer to the stream to read KTX data from.
800 * @param[in] createFlags bitmask requesting specific actions during creation.
801 * @param[in,out] newTex pointer to a location in which store the address of
802 * the newly created texture.
803 *
804 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
805 *
806 * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
807 *
808 * For other exceptions, see ktxTexture_CreateFromStdioStream().
809 */
810KTX_error_code
811ktxTexture1_CreateFromStream(ktxStream* stream,
812 ktxTextureCreateFlags createFlags,
813 ktxTexture1** newTex)
814{
815 KTX_error_code result;
816 if (newTex == NULL)
817 return KTX_INVALID_VALUE;
818
819 ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
820 if (tex == NULL)
821 return KTX_OUT_OF_MEMORY;
822
823 result = ktxTexture1_constructFromStream(tex, stream, createFlags);
824 if (result == KTX_SUCCESS)
825 *newTex = (ktxTexture1*)tex;
826 else {
827 free(tex);
828 *newTex = NULL;
829 }
830 return result;
831}
832
833/**
834 * @memberof ktxTexture1
835 * @~English
836 * @brief Destroy a ktxTexture1 object.
837 *
838 * This frees the memory associated with the texture contents and the memory
839 * of the ktxTexture1 object. This does @e not delete any OpenGL or Vulkan
840 * texture objects created by ktxTexture1_GLUpload or ktxTexture1_VkUpload.
841 *
842 * @param[in] This pointer to the ktxTexture1 object to destroy
843 */
844void
845ktxTexture1_Destroy(ktxTexture1* This)
846{
847 ktxTexture1_destruct(This);
848 free(This);
849}
850
851/**
852 * @memberof ktxTexture @private
853 * @~English
854 * @brief Calculate the size of the image data for the specified number
855 * of levels.
856 *
857 * The data size is the sum of the sizes of each level up to the number
858 * specified and includes any @c mipPadding.
859 *
860 * @param[in] This pointer to the ktxTexture object of interest.
861 * @param[in] levels number of levels whose data size to return.
862 *
863 * @return the data size in bytes.
864 */
865ktx_size_t
866ktxTexture1_calcDataSizeLevels(ktxTexture1* This, ktx_uint32_t levels)
867{
868 ktx_uint32_t i;
869 ktx_size_t dataSize = 0;
870
871 assert(This != NULL);
872 assert(levels <= This->numLevels);
873 for (i = 0; i < levels; i++) {
874 ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,
875 KTX_FORMAT_VERSION_ONE);
876 /* mipPadding. NOTE: this adds padding after the last level too. */
877 #if KTX_GL_UNPACK_ALIGNMENT != 4
878 dataSize += _KTX_PAD4(levelSize);
879 #else
880 dataSize += levelSize;
881 #endif
882 }
883 return dataSize;
884}
885
886/**
887 * @memberof ktxTexture1 @private
888 * @~English
889 *
890 * @copydoc ktxTexture::ktxTexture_doCalcFaceLodSize
891 */
892ktx_size_t
893ktxTexture1_calcFaceLodSize(ktxTexture1* This, ktx_uint32_t level)
894{
895 return ktxTexture_doCalcFaceLodSize(ktxTexture(This), level,
896 KTX_FORMAT_VERSION_ONE);
897}
898
899/**
900 * @memberof ktxTexture @private
901 * @~English
902 * @brief Return the offset of a level in bytes from the start of the image
903 * data in a ktxTexture.
904 *
905 * The caclulated size does not include space for storing the @c imageSize
906 * fields of each mip level.
907 *
908 * @param[in] This pointer to the ktxTexture object of interest.
909 * @param[in] level level whose offset to return.
910 * @param[in] fv enum specifying format version for which to calculate
911 * image size.
912 *
913 * @return the data size in bytes.
914 */
915ktx_size_t
916ktxTexture1_calcLevelOffset(ktxTexture1* This, ktx_uint32_t level)
917{
918 assert (This != NULL);
919 assert (level < This->numLevels);
920 return ktxTexture1_calcDataSizeLevels(This, level);
921}
922
923/**
924 * @memberof ktxTexture1
925 * @~English
926 * @brief Find the offset of an image within a ktxTexture's image data.
927 *
928 * As there is no such thing as a 3D cubemap we make the 3rd location parameter
929 * do double duty.
930 *
931 * @param[in] This pointer to the ktxTexture object of interest.
932 * @param[in] level mip level of the image.
933 * @param[in] layer array layer of the image.
934 * @param[in] faceSlice cube map face or depth slice of the image.
935 * @param[in,out] pOffset pointer to location to store the offset.
936 *
937 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
938 *
939 * @exception KTX_INVALID_OPERATION
940 * @p level, @p layer or @p faceSlice exceed the
941 * dimensions of the texture.
942 * @exception KTX_INVALID_VALID @p This is NULL.
943 */
944KTX_error_code
945ktxTexture1_GetImageOffset(ktxTexture1* This, ktx_uint32_t level,
946 ktx_uint32_t layer, ktx_uint32_t faceSlice,
947 ktx_size_t* pOffset)
948{
949
950 if (This == NULL)
951 return KTX_INVALID_VALUE;
952
953 if (level >= This->numLevels || layer >= This->numLayers)
954 return KTX_INVALID_OPERATION;
955
956 if (This->isCubemap) {
957 if (faceSlice >= This->numFaces)
958 return KTX_INVALID_OPERATION;
959 } else {
960 ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level);
961 if (faceSlice >= maxSlice)
962 return KTX_INVALID_OPERATION;
963 }
964
965 // Get the size of the data up to the start of the indexed level.
966 *pOffset = ktxTexture_calcDataSizeLevels(ktxTexture(This), level);
967
968 // All layers, faces & slices within a level are the same size.
969 if (layer != 0) {
970 ktx_size_t layerSize;
971 layerSize = ktxTexture_layerSize(ktxTexture(This), level,
972 KTX_FORMAT_VERSION_ONE);
973 *pOffset += layer * layerSize;
974 }
975 if (faceSlice != 0) {
976 ktx_size_t imageSize;
977 imageSize = ktxTexture_GetImageSize(ktxTexture(This), level);
978#if (KTX_GL_UNPACK_ALIGNMENT != 4)
979 if (This->isCubemap)
980 _KTX_PAD4(imageSize); // Account for cubePadding.
981#endif
982 *pOffset += faceSlice * imageSize;
983 }
984
985 return KTX_SUCCESS;
986}
987
988/**
989 * @memberof ktxTexture1
990 * @~English
991 * @brief Return the total size in bytes of the uncompressed data of a ktxTexture1.
992 *
993 * This always returns the value of @c This->dataSize. The function is provided for
994 * symmetry with ktxTexture2.
995 *
996 * @param[in] This pointer to the ktxTexture1 object of interest.
997 * @return The size of the data in the texture.
998 */
999ktx_size_t
1000ktxTexture1_GetDataSizeUncompressed(ktxTexture1* This)
1001{
1002 return This->dataSize;
1003}
1004
1005/**
1006 * @memberof ktxTexture1
1007 * @~English
1008 * @brief Calculate & return the size in bytes of an image at the specified
1009 * mip level.
1010 *
1011 * For arrays, this is the size of layer, for cubemaps, the size of a face
1012 * and for 3D textures, the size of a depth slice.
1013 *
1014 * The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.
1015 *
1016 * @param[in] This pointer to the ktxTexture1 object of interest.
1017 * @param[in] level level of interest.
1018 */
1019ktx_size_t
1020ktxTexture1_GetImageSize(ktxTexture1* This, ktx_uint32_t level)
1021{
1022 return ktxTexture_calcImageSize(ktxTexture(This), level,
1023 KTX_FORMAT_VERSION_ONE);
1024}
1025
1026/**
1027 * @memberof ktxTexture1 @private
1028 * @~English
1029 * @brief Return the size of the primitive type of a single color component
1030 *
1031 * @param[in] This pointer to the ktxTexture1 object of interest.
1032 *
1033 * @return the type size in bytes.
1034 */
1035ktx_uint32_t
1036ktxTexture1_glTypeSize(ktxTexture1* This)
1037{
1038 assert(This != NULL);
1039 return This->_protected->_typeSize;
1040}
1041
1042/**
1043 * @memberof ktxTexture1
1044 * @~English
1045 * @brief Iterate over the mip levels in a ktxTexture1 object.
1046 *
1047 * This is almost identical to ktxTexture_IterateLevelFaces(). The difference is
1048 * that the blocks of image data for non-array cube maps include all faces of
1049 * a mip level.
1050 *
1051 * This function works even if @p This->pData == 0 so it can be used to
1052 * obtain offsets and sizes for each level by callers who have loaded the data
1053 * externally.
1054 *
1055 * @param[in] This handle of the 1 opened on the data.
1056 * @param[in,out] iterCb the address of a callback function which is called
1057 * with the data for each image block.
1058 * @param[in,out] userdata the address of application-specific data which is
1059 * passed to the callback along with the image data.
1060 *
1061 * @return KTX_SUCCESS on success, other KTX_* enum values on error. The
1062 * following are returned directly by this function. @p iterCb may
1063 * return these for other causes or may return additional errors.
1064 *
1065 * @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not
1066 * decreasing
1067 * @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.
1068 *
1069 */
1070KTX_error_code
1071ktxTexture1_IterateLevels(ktxTexture1* This, PFNKTXITERCB iterCb, void* userdata)
1072{
1073 ktx_uint32_t miplevel;
1074 KTX_error_code result = KTX_SUCCESS;
1075
1076 if (This == NULL)
1077 return KTX_INVALID_VALUE;
1078
1079 if (iterCb == NULL)
1080 return KTX_INVALID_VALUE;
1081
1082 for (miplevel = 0; miplevel < This->numLevels; ++miplevel)
1083 {
1084 GLsizei width, height, depth;
1085 ktx_uint32_t levelSize;
1086 ktx_size_t offset;
1087
1088 /* Array textures have the same number of layers at each mip level. */
1089 width = MAX(1, This->baseWidth >> miplevel);
1090 height = MAX(1, This->baseHeight >> miplevel);
1091 depth = MAX(1, This->baseDepth >> miplevel);
1092
1093 levelSize = (ktx_uint32_t)ktxTexture_calcLevelSize(ktxTexture(This),
1094 miplevel,
1095 KTX_FORMAT_VERSION_ONE);
1096
1097 /* All array layers are passed in a group because that is how
1098 * GL & Vulkan need them. Hence no
1099 * for (layer = 0; layer < This->numLayers)
1100 */
1101 ktxTexture_GetImageOffset(ktxTexture(This), miplevel, 0, 0, &offset);
1102 result = iterCb(miplevel, 0, width, height, depth,
1103 levelSize, This->pData + offset, userdata);
1104 if (result != KTX_SUCCESS)
1105 break;
1106 }
1107
1108 return result;
1109}
1110
1111/**
1112 * @memberof ktxTexture1
1113 * @~English
1114 * @brief Iterate over the images in a ktxTexture1 object while loading the
1115 * image data.
1116 *
1117 * This operates similarly to ktxTexture_IterateLevelFaces() except that it
1118 * loads the images from the ktxTexture1's source to a temporary buffer
1119 * while iterating. The callback function must copy the image data if it
1120 * wishes to preserve it as the temporary buffer is reused for each level and
1121 * is freed when this function exits.
1122 *
1123 * This function is helpful for reducing memory usage when uploading the data
1124 * to a graphics API.
1125 *
1126 * @param[in] This pointer to the ktxTexture1 object of interest.
1127 * @param[in,out] iterCb the address of a callback function which is called
1128 * with the data for each image.
1129 * @param[in,out] userdata the address of application-specific data which is
1130 * passed to the callback along with the image data.
1131 *
1132 * @return KTX_SUCCESS on success, other KTX_* enum values on error. The
1133 * following are returned directly by this function. @p iterCb may
1134 * return these for other causes or may return additional errors.
1135 *
1136 * @exception KTX_FILE_DATA_ERROR mip level sizes are increasing not
1137 * decreasing
1138 * @exception KTX_INVALID_OPERATION the ktxTexture1 was not created from a
1139 * stream, i.e there is no data to load, or
1140 * this ktxTexture1's images have already
1141 * been loaded.
1142 * @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.
1143 * @exception KTX_OUT_OF_MEMORY not enough memory to allocate a block to
1144 * hold the base level image.
1145 */
1146KTX_error_code
1147ktxTexture1_IterateLoadLevelFaces(ktxTexture1* This, PFNKTXITERCB iterCb,
1148 void* userdata)
1149{
1150 DECLARE_PRIVATE(ktxTexture1);
1151 struct ktxTexture_protected* prtctd = This->_protected;
1152 ktxStream* stream = (ktxStream *)&prtctd->_stream;
1153 ktx_uint32_t dataSize = 0;
1154 ktx_uint32_t miplevel;
1155 KTX_error_code result = KTX_SUCCESS;
1156 void* data = NULL;
1157
1158 if (This == NULL)
1159 return KTX_INVALID_VALUE;
1160
1161 if (This->classId != ktxTexture1_c)
1162 return KTX_INVALID_OPERATION;
1163
1164 if (iterCb == NULL)
1165 return KTX_INVALID_VALUE;
1166
1167 if (prtctd->_stream.data.file == NULL)
1168 // This Texture not created from a stream or images are already loaded.
1169 return KTX_INVALID_OPERATION;
1170
1171 for (miplevel = 0; miplevel < This->numLevels; ++miplevel)
1172 {
1173 ktx_uint32_t faceLodSize;
1174 ktx_uint32_t faceLodSizePadded;
1175 ktx_uint32_t face;
1176 ktx_uint32_t innerIterations;
1177 GLsizei width, height, depth;
1178
1179 /* Array textures have the same number of layers at each mip level. */
1180 width = MAX(1, This->baseWidth >> miplevel);
1181 height = MAX(1, This->baseHeight >> miplevel);
1182 depth = MAX(1, This->baseDepth >> miplevel);
1183
1184 result = stream->read(stream, &faceLodSize, sizeof(ktx_uint32_t));
1185 if (result != KTX_SUCCESS) {
1186 goto cleanup;
1187 }
1188 if (private->_needSwap) {
1189 _ktxSwapEndian32(&faceLodSize, 1);
1190 }
1191#if (KTX_GL_UNPACK_ALIGNMENT != 4)
1192 faceLodSizePadded = _KTX_PAD4(faceLodSize);
1193#else
1194 faceLodSizePadded = faceLodSize;
1195#endif
1196 if (!data) {
1197 /* allocate memory sufficient for the base miplevel */
1198 data = malloc(faceLodSizePadded);
1199 if (!data) {
1200 result = KTX_OUT_OF_MEMORY;
1201 goto cleanup;
1202 }
1203 dataSize = faceLodSizePadded;
1204 }
1205 else if (dataSize < faceLodSizePadded) {
1206 /* subsequent miplevels cannot be larger than the base miplevel */
1207 result = KTX_FILE_DATA_ERROR;
1208 goto cleanup;
1209 }
1210
1211 /* All array layers are passed in a group because that is how
1212 * GL & Vulkan need them. Hence no
1213 * for (layer = 0; layer < This->numLayers)
1214 */
1215 if (This->isCubemap && !This->isArray)
1216 innerIterations = This->numFaces;
1217 else
1218 innerIterations = 1;
1219 for (face = 0; face < innerIterations; ++face)
1220 {
1221 /* And all z_slices are also passed as a group hence no
1222 * for (z_slice = 0; z_slice < This->depth)
1223 */
1224 result = stream->read(stream, data, faceLodSizePadded);
1225 if (result != KTX_SUCCESS) {
1226 goto cleanup;
1227 }
1228
1229 /* Perform endianness conversion on texture data */
1230 if (private->_needSwap) {
1231 if (prtctd->_typeSize == 2)
1232 _ktxSwapEndian16((ktx_uint16_t*)data, faceLodSize / 2);
1233 else if (prtctd->_typeSize == 4)
1234 _ktxSwapEndian32((ktx_uint32_t*)data, faceLodSize / 4);
1235 }
1236
1237 result = iterCb(miplevel, face,
1238 width, height, depth,
1239 faceLodSize, data, userdata);
1240 }
1241 }
1242
1243cleanup:
1244 free(data);
1245 // No further need for this.
1246 stream->destruct(stream);
1247
1248 return result;
1249}
1250
1251/**
1252 * @memberof ktxTexture1
1253 * @~English
1254 * @brief Load all the image data from the ktxTexture1's source.
1255 *
1256 * The data is loaded into the provided buffer or to an internally allocated
1257 * buffer, if @p pBuffer is @c NULL.
1258 *
1259 * @param[in] This pointer to the ktxTexture object of interest.
1260 * @param[in] pBuffer pointer to the buffer in which to load the image data.
1261 * @param[in] bufSize size of the buffer pointed at by @p pBuffer.
1262 *
1263 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
1264 *
1265 * @exception KTX_INVALID_VALUE @p This is NULL.
1266 * @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.
1267 * @exception KTX_INVALID_OPERATION
1268 * The data has already been loaded or the
1269 * ktxTexture was not created from a KTX source.
1270 * @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.
1271 */
1272KTX_error_code
1273ktxTexture1_LoadImageData(ktxTexture1* This,
1274 ktx_uint8_t* pBuffer, ktx_size_t bufSize)
1275{
1276 DECLARE_PROTECTED(ktxTexture);
1277 DECLARE_PRIVATE(ktxTexture1);
1278 ktx_uint32_t miplevel;
1279 ktx_uint8_t* pDest;
1280 KTX_error_code result = KTX_SUCCESS;
1281
1282 if (This == NULL)
1283 return KTX_INVALID_VALUE;
1284
1285 if (prtctd->_stream.data.file == NULL)
1286 // This Texture not created from a stream or images already loaded;
1287 return KTX_INVALID_OPERATION;
1288
1289 if (pBuffer == NULL) {
1290 This->pData = malloc(This->dataSize);
1291 if (This->pData == NULL)
1292 return KTX_OUT_OF_MEMORY;
1293 pDest = This->pData;
1294 } else if (bufSize < This->dataSize) {
1295 return KTX_INVALID_VALUE;
1296 } else {
1297 pDest = pBuffer;
1298 }
1299
1300 // Need to loop through for correct byte swapping
1301 for (miplevel = 0; miplevel < This->numLevels; ++miplevel)
1302 {
1303 ktx_uint32_t faceLodSize;
1304 ktx_uint32_t faceLodSizePadded;
1305 ktx_uint32_t face;
1306 ktx_uint32_t innerIterations;
1307
1308 result = prtctd->_stream.read(&prtctd->_stream, &faceLodSize,
1309 sizeof(ktx_uint32_t));
1310 if (result != KTX_SUCCESS) {
1311 goto cleanup;
1312 }
1313 if (private->_needSwap) {
1314 _ktxSwapEndian32(&faceLodSize, 1);
1315 }
1316#if (KTX_GL_UNPACK_ALIGNMENT != 4)
1317 faceLodSizePadded = _KTX_PAD4(faceLodSize);
1318#else
1319 faceLodSizePadded = faceLodSize;
1320#endif
1321
1322 if (This->isCubemap && !This->isArray)
1323 innerIterations = This->numFaces;
1324 else
1325 innerIterations = 1;
1326 for (face = 0; face < innerIterations; ++face)
1327 {
1328 result = prtctd->_stream.read(&prtctd->_stream, pDest,
1329 faceLodSizePadded);
1330 if (result != KTX_SUCCESS) {
1331 goto cleanup;
1332 }
1333
1334 /* Perform endianness conversion on texture data */
1335 if (private->_needSwap) {
1336 if (prtctd->_typeSize == 2)
1337 _ktxSwapEndian16((ktx_uint16_t*)pDest, faceLodSize / 2);
1338 else if (prtctd->_typeSize == 4)
1339 _ktxSwapEndian32((ktx_uint32_t*)pDest, faceLodSize / 4);
1340 }
1341
1342 pDest += faceLodSizePadded;
1343 }
1344 }
1345
1346cleanup:
1347 // No further need for This->
1348 prtctd->_stream.destruct(&prtctd->_stream);
1349 return result;
1350}
1351
1352ktx_bool_t
1353ktxTexture1_NeedsTranscoding(ktxTexture1* This)
1354{
1355 UNUSED(This);
1356 return KTX_FALSE;
1357}
1358
1359#if !KTX_FEATURE_WRITE
1360
1361/*
1362 * Stubs for writer functions that return a proper error code
1363 */
1364
1365KTX_error_code
1366ktxTexture1_SetImageFromMemory(ktxTexture1* This, ktx_uint32_t level,
1367 ktx_uint32_t layer, ktx_uint32_t faceSlice,
1368 const ktx_uint8_t* src, ktx_size_t srcSize)
1369{
1370 UNUSED(This);
1371 UNUSED(level);
1372 UNUSED(layer);
1373 UNUSED(faceSlice);
1374 UNUSED(src);
1375 UNUSED(srcSize);
1376 return KTX_INVALID_OPERATION;
1377}
1378
1379KTX_error_code
1380ktxTexture1_SetImageFromStdioStream(ktxTexture1* This, ktx_uint32_t level,
1381 ktx_uint32_t layer, ktx_uint32_t faceSlice,
1382 FILE* src, ktx_size_t srcSize)
1383{
1384 UNUSED(This);
1385 UNUSED(level);
1386 UNUSED(layer);
1387 UNUSED(faceSlice);
1388 UNUSED(src);
1389 UNUSED(srcSize);
1390 return KTX_INVALID_OPERATION;
1391}
1392
1393KTX_error_code
1394ktxTexture1_WriteToStdioStream(ktxTexture1* This, FILE* dstsstr)
1395{
1396 UNUSED(This);
1397 UNUSED(dstsstr);
1398 return KTX_INVALID_OPERATION;
1399}
1400
1401KTX_error_code
1402ktxTexture1_WriteToNamedFile(ktxTexture1* This, const char* const dstname)
1403{
1404 UNUSED(This);
1405 UNUSED(dstname);
1406 return KTX_INVALID_OPERATION;
1407}
1408
1409KTX_error_code
1410ktxTexture1_WriteToMemory(ktxTexture1* This,
1411 ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)
1412{
1413 UNUSED(This);
1414 UNUSED(ppDstBytes);
1415 UNUSED(pSize);
1416 return KTX_INVALID_OPERATION;
1417}
1418
1419KTX_error_code
1420ktxTexture1_WriteToStream(ktxTexture1* This,
1421 ktxStream* dststr)
1422{
1423 UNUSED(This);
1424 UNUSED(dststr);
1425 return KTX_INVALID_OPERATION;
1426}
1427
1428#endif
1429
1430/*
1431 * Initialized here at the end to avoid the need for multiple declarations of
1432 * these functions.
1433 */
1434
1435struct ktxTexture_vtblInt ktxTexture1_vtblInt = {
1436 (PFNCALCDATASIZELEVELS)ktxTexture1_calcDataSizeLevels,
1437 (PFNCALCFACELODSIZE)ktxTexture1_calcFaceLodSize,
1438 (PFNCALCLEVELOFFSET)ktxTexture1_calcLevelOffset
1439};
1440
1441struct ktxTexture_vtbl ktxTexture1_vtbl = {
1442 (PFNKTEXDESTROY)ktxTexture1_Destroy,
1443 (PFNKTEXGETIMAGEOFFSET)ktxTexture1_GetImageOffset,
1444 (PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture1_GetDataSizeUncompressed,
1445 (PFNKTEXGETIMAGESIZE)ktxTexture1_GetImageSize,
1446 (PFNKTEXITERATELEVELS)ktxTexture1_IterateLevels,
1447 (PFNKTEXITERATELOADLEVELFACES)ktxTexture1_IterateLoadLevelFaces,
1448 (PFNKTEXNEEDSTRANSCODING)ktxTexture1_NeedsTranscoding,
1449 (PFNKTEXLOADIMAGEDATA)ktxTexture1_LoadImageData,
1450 (PFNKTEXSETIMAGEFROMMEMORY)ktxTexture1_SetImageFromMemory,
1451 (PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture1_SetImageFromStdioStream,
1452 (PFNKTEXWRITETOSTDIOSTREAM)ktxTexture1_WriteToStdioStream,
1453 (PFNKTEXWRITETONAMEDFILE)ktxTexture1_WriteToNamedFile,
1454 (PFNKTEXWRITETOMEMORY)ktxTexture1_WriteToMemory,
1455 (PFNKTEXWRITETOSTREAM)ktxTexture1_WriteToStream,
1456};
1457
1458/** @} */
1459
1460