diff options
author | acazuc <acazuc@acazuc.fr> | 2023-04-28 18:26:01 +0200 |
---|---|---|
committer | acazuc <acazuc@acazuc.fr> | 2023-08-19 10:27:29 +0200 |
commit | a00cf02241a1e7772ae1c44c63d95cae08bd53a0 (patch) | |
tree | 17beb0dda28acb90d494211f8cdcbd0e89fbd57d /thirdparty/libktx/lib/memstream.c | |
parent | ff5c884153d1c15bde3ee87aa295e52bec3a6340 (diff) | |
download | redot-engine-a00cf02241a1e7772ae1c44c63d95cae08bd53a0.tar.gz |
Add support for KTX & KTX2 image format
Add support glTF KHR_texture_basisu extension
Diffstat (limited to 'thirdparty/libktx/lib/memstream.c')
-rw-r--r-- | thirdparty/libktx/lib/memstream.c | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/thirdparty/libktx/lib/memstream.c b/thirdparty/libktx/lib/memstream.c new file mode 100644 index 0000000000..b963fa70ca --- /dev/null +++ b/thirdparty/libktx/lib/memstream.c @@ -0,0 +1,561 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +/* + * Copyright 2010-2020 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @~English + * + * @brief Implementation of ktxStream for memory. + * + * @author Maksim Kolesin, Under Development + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +#include "ktx.h" +#include "ktxint.h" +#include "memstream.h" + +/** +* @brief Default allocation size for a ktxMemStream. +*/ +#define KTX_MEM_DEFAULT_ALLOCATED_SIZE 256 + +/** + * @brief Structure to store information about data allocated for ktxMemStream. + */ +struct ktxMem +{ + const ktx_uint8_t* robytes;/*!< pointer to read-only data */ + ktx_uint8_t* bytes; /*!< pointer to rw data. */ + ktx_size_t alloc_size; /*!< allocated size of the memory block. */ + ktx_size_t used_size; /*!< bytes used. Effectively the write position. */ + ktx_off_t pos; /*!< read/write position. */ +}; + +static KTX_error_code ktxMem_expand(ktxMem* pMem, const ktx_size_t size); + +/** + * @brief Initialize a ktxMem struct for read-write. + * + * Memory for the stream data is allocated internally but the + * caller is responsible for freeing the memory. A pointer to + * the memory can be obtained with ktxMem_getdata(). + * + * @sa ktxMem_getdata. + * + * @param [in] pMem pointer to the @c ktxMem to initialize. + */ +static KTX_error_code +ktxMem_construct(ktxMem* pMem) +{ + pMem->pos = 0; + pMem->alloc_size = 0; + pMem->robytes = 0; + pMem->bytes = 0; + pMem->used_size = 0; + return ktxMem_expand(pMem, KTX_MEM_DEFAULT_ALLOCATED_SIZE); +} + +/** + * @brief Create & initialize a ktxMem struct for read-write. + * + * @sa ktxMem_construct. + * + * @param [in,out] ppMem pointer to the location in which to return + * a pointer to the newly created @c ktxMem. + * + * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error. + * + * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory. + */ +static KTX_error_code +ktxMem_create(ktxMem** ppMem) +{ + ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem)); + if (pNewMem) { + KTX_error_code result = ktxMem_construct(pNewMem); + if (result == KTX_SUCCESS) + *ppMem = pNewMem; + return result; + } + else { + return KTX_OUT_OF_MEMORY; + } +} + +/** + * @brief Initialize a ktxMem struct for read-only. + * + * @param [in] pMem pointer to the @c ktxMem to initialize. + * @param [in] bytes pointer to the data to be read. + * @param [in] numBytes number of bytes of data. + */ +static void +ktxMem_construct_ro(ktxMem* pMem, const void* bytes, ktx_size_t numBytes) +{ + pMem->pos = 0; + pMem->robytes = bytes; + pMem->bytes = 0; + pMem->used_size = numBytes; + pMem->alloc_size = numBytes; +} + +/** + * @brief Create & initialize a ktxMem struct for read-only. + * + * @sa ktxMem_construct. + * + * @param [in,out] ppMem pointer to the location in which to return + * a pointer to the newly created @c ktxMem. + * @param [in] bytes pointer to the data to be read. + * @param [in] numBytes number of bytes of data. + * + * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error. + * + * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory. + */ +static KTX_error_code +ktxMem_create_ro(ktxMem** ppMem, const void* bytes, ktx_size_t numBytes) +{ + ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem)); + if (pNewMem) { + ktxMem_construct_ro(pNewMem, bytes, numBytes); + *ppMem = pNewMem; + return KTX_SUCCESS; + } + else { + return KTX_OUT_OF_MEMORY; + } +} + +/* + * ktxMem_destruct not needed as ktxMem_construct caller is reponsible + * for freeing the data written. + */ + +/** + * @brief Free the memory of a struct ktxMem. + * + * @param pMem pointer to ktxMem to free. + */ +static void +ktxMem_destroy(ktxMem* pMem, ktx_bool_t freeData) +{ + assert(pMem != NULL); + if (freeData) { + free(pMem->bytes); + } + free(pMem); +} + +#ifdef KTXMEM_CLEAR_USED +/** + * @brief Clear the data of a memory stream. + * + * @param pMem pointer to ktxMem to clear. + */ +static void +ktxMem_clear(ktxMem* pMem) +{ + assert(pMem != NULL); + memset(pMem, 0, sizeof(ktxMem)); +} +#endif + +/** + * @~English + * @brief Expand a ktxMem to fit to a new size. + * + * @param [in] pMem pointer to ktxMem struct to expand. + * @param [in] newsize minimum new size required. + * + * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error. + * + * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient pMemory. + */ +static KTX_error_code +ktxMem_expand(ktxMem *pMem, const ktx_size_t newsize) +{ + ktx_size_t new_alloc_size; + + assert(pMem != NULL && newsize != 0); + + new_alloc_size = pMem->alloc_size == 0 ? + KTX_MEM_DEFAULT_ALLOCATED_SIZE : pMem->alloc_size; + while (new_alloc_size < newsize) { + ktx_size_t alloc_size = new_alloc_size; + new_alloc_size <<= 1; + if (new_alloc_size < alloc_size) { + /* Overflow. Set to maximum size. newsize can't be larger. */ + new_alloc_size = (ktx_size_t)-1L; + } + } + + if (new_alloc_size == pMem->alloc_size) + return KTX_SUCCESS; + + if (!pMem->bytes) + pMem->bytes = (ktx_uint8_t*)malloc(new_alloc_size); + else + pMem->bytes = (ktx_uint8_t*)realloc(pMem->bytes, new_alloc_size); + + if (!pMem->bytes) + { + pMem->alloc_size = 0; + pMem->used_size = 0; + return KTX_OUT_OF_MEMORY; + } + + pMem->alloc_size = new_alloc_size; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Read bytes from a ktxMemStream. + * + * @param [in] str pointer to ktxMem struct, converted to a void*, that + * specifies an input stream. + * @param [in,out] dst pointer to memory where to copy read bytes. + * @param [in,out] count pointer to number of bytes to read. + * + * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error. + * + * @exception KTX_INVALID_VALUE @p str or @p dst is @c NULL or @p str->data is + * @c NULL. + * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request. + */ +static +KTX_error_code ktxMemStream_read(ktxStream* str, void* dst, const ktx_size_t count) +{ + ktxMem* mem; + ktx_off_t newpos; + const ktx_uint8_t* bytes; + + + if (!str || (mem = str->data.mem)== 0) + return KTX_INVALID_VALUE; + + newpos = mem->pos + count; + /* The first clause checks for overflow. */ + if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size) + return KTX_FILE_UNEXPECTED_EOF; + + bytes = mem->robytes ? mem->robytes : mem->bytes; + memcpy(dst, bytes + mem->pos, count); + mem->pos = newpos; + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Skip bytes in a ktxMemStream. + * + * @param [in] str pointer to the ktxStream on which to operate. + * @param [in] count number of bytes to skip. + * + * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error. + * + * @exception KTX_INVALID_VALUE @p str or @p mem is @c NULL or sufficient + * data is not available in ktxMem. + * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request. + */ +static +KTX_error_code ktxMemStream_skip(ktxStream* str, const ktx_size_t count) +{ + ktxMem* mem; + ktx_off_t newpos; + + if (!str || (mem = str->data.mem) == 0) + return KTX_INVALID_VALUE; + + newpos = mem->pos + count; + /* The first clause checks for overflow. */ + if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size) + return KTX_FILE_UNEXPECTED_EOF; + + mem->pos = newpos; + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Write bytes to a ktxMemStream. + * + * @param [out] str pointer to the ktxStream that specifies the destination. + * @param [in] src pointer to the array of elements to be written, + * converted to a const void*. + * @param [in] size size in bytes of each element to be written. + * @param [in] count number of elements, each one with a @p size of size + * bytes. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_OVERFLOW write would result in file exceeding the + * maximum permissible size. + * @exception KTX_INVALID_OPERATION @p str is a read-only stream. + * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p mem is @c NULL. + * @exception KTX_OUT_OF_MEMORY See ktxMem_expand() for causes. + */ +static +KTX_error_code ktxMemStream_write(ktxStream* str, const void* src, + const ktx_size_t size, const ktx_size_t count) +{ + ktxMem* mem; + KTX_error_code rc = KTX_SUCCESS; + ktx_size_t new_size; + + if (!str || (mem = str->data.mem) == 0) + return KTX_INVALID_VALUE; + + if (mem->robytes) + return KTX_INVALID_OPERATION; /* read-only */ + + new_size = mem->pos + (size*count); + //if (new_size < mem->used_size) + if ((ktx_off_t)new_size < mem->pos) + return KTX_FILE_OVERFLOW; + + if (mem->alloc_size < new_size) { + rc = ktxMem_expand(mem, new_size); + if (rc != KTX_SUCCESS) + return rc; + } + + memcpy(mem->bytes + mem->pos, src, size*count); + mem->pos += size*count; + if (mem->pos > (ktx_off_t)mem->used_size) + mem->used_size = mem->pos; + + + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Get the current read/write position in a ktxMemStream. + * + * @param [in] str pointer to the ktxStream to query. + * @param [in,out] off pointer to variable to receive the offset value. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str or @p pos is @c NULL. + */ +static +KTX_error_code ktxMemStream_getpos(ktxStream* str, ktx_off_t* const pos) +{ + if (!str || !pos) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeMemory); + + *pos = str->data.mem->pos; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Set the current read/write position in a ktxMemStream. + * + * Offset of 0 is the start of the file. + * + * @param [in] str pointer to the ktxStream whose r/w position is to be set. + * @param [in] off pointer to the offset value to set. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str is @c NULL. + * @exception KTX_INVALID_OPERATION @p pos > size of the allocated memory. + */ +static +KTX_error_code ktxMemStream_setpos(ktxStream* str, ktx_off_t pos) +{ + if (!str) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeMemory); + + if (pos > (ktx_off_t)str->data.mem->alloc_size) + return KTX_INVALID_OPERATION; + + str->data.mem->pos = pos; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Get a pointer to a ktxMemStream's data. + * + * Gets a pointer to data that has been written to the stream. Returned + * pointer will be 0 if stream is read-only. + * + * @param [in] str pointer to the ktxStream whose data pointer is to + * be queried. + * @param [in,out] ppBytes pointer to a variable in which the data pointer + * will be written. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str or @p ppBytes is @c NULL. + */ +KTX_error_code ktxMemStream_getdata(ktxStream* str, ktx_uint8_t** ppBytes) +{ + if (!str || !ppBytes) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeMemory); + + *ppBytes = str->data.mem->bytes; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Get the size of a ktxMemStream in bytes. + * + * @param [in] str pointer to the ktxStream whose size is to be queried. + * @param [in,out] size pointer to a variable in which size will be written. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str or @p pSize is @c NULL. + */ +static +KTX_error_code ktxMemStream_getsize(ktxStream* str, ktx_size_t* pSize) +{ + if (!str || !pSize) + return KTX_INVALID_VALUE; + + assert(str->type == eStreamTypeMemory); + + *pSize = str->data.mem->used_size; + return KTX_SUCCESS; +} + +/** + * @~English + * @brief Setup ktxMemStream function pointers. + */ +void +ktxMemStream_setup(ktxStream* str) +{ + str->type = eStreamTypeMemory; + str->read = ktxMemStream_read; + str->skip = ktxMemStream_skip; + str->write = ktxMemStream_write; + str->getpos = ktxMemStream_getpos; + str->setpos = ktxMemStream_setpos; + str->getsize = ktxMemStream_getsize; + str->destruct = ktxMemStream_destruct; +} + +/** + * @~English + * @brief Initialize a read-write ktxMemStream. + * + * Memory is allocated as data is written. The caller of this is + * responsible for freeing this memory unless @a freeOnDestruct + * is not KTX_FALSE. + * + * @param [in] str pointer to a ktxStream struct to initialize. + * @param [in] freeOnDestruct If not KTX_FALSE memory holding the data will + * be freed by the destructor. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str is @c NULL. + * @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory. + */ +KTX_error_code ktxMemStream_construct(ktxStream* str, + ktx_bool_t freeOnDestruct) +{ + ktxMem* mem; + KTX_error_code result = KTX_SUCCESS; + + if (!str) + return KTX_INVALID_VALUE; + + result = ktxMem_create(&mem); + + if (KTX_SUCCESS == result) { + str->data.mem = mem; + ktxMemStream_setup(str); + str->closeOnDestruct = freeOnDestruct; + } + + return result; +} + +/** + * @~English + * @brief Initialize a read-only ktxMemStream. + * + * @param [in] str pointer to a ktxStream struct to initialize. + * @param [in] bytes pointer to an array of bytes containing the data. + * @param [in] numBytes size of array of data for ktxMemStream. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p str or @p mem is @c NULL or @p numBytes + * is 0. + * or @p size is less than 0. + * @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory. + */ +KTX_error_code ktxMemStream_construct_ro(ktxStream* str, + const ktx_uint8_t* bytes, + const ktx_size_t numBytes) +{ + ktxMem* mem; + KTX_error_code result = KTX_SUCCESS; + + if (!str || !bytes || numBytes == 0) + return KTX_INVALID_VALUE; + + result = ktxMem_create_ro(&mem, bytes, numBytes); + + if (KTX_SUCCESS == result) { + str->data.mem = mem; + ktxMemStream_setup(str); + str->closeOnDestruct = KTX_FALSE; + } + + return result; +} + +/** + * @~English + * @brief Free the memory used by a ktxMemStream. + * + * This only frees the memory used to store the data written to the stream, + * if the @c freeOnDestruct parameter to ktxMemStream_construct() was not + * @c KTX_FALSE. Otherwise it is the responsibility of the caller of + * ktxMemStream_construct() and a pointer to this memory should be retrieved + * using ktxMemStream_getdata() before calling this function. + * + * @sa ktxMemStream_construct, ktxMemStream_getdata. + * + * @param [in] str pointer to the ktxStream whose memory is + * to be freed. + */ +void +ktxMemStream_destruct(ktxStream* str) +{ + assert(str && str->type == eStreamTypeMemory); + + ktxMem_destroy(str->data.mem, str->closeOnDestruct); + str->data.mem = NULL; +} + |