From 006ab90c67111ea744eaeb37439ad0bc78d58d07 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 4 Jul 2019 14:30:28 +0900 Subject: [PATCH] Initial support of KTX texture load using TinyKTX. --- README.md | 12 +- tiny_gltf.h | 243 +++++++- tinyktx.h | 1600 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1846 insertions(+), 9 deletions(-) create mode 100644 tinyktx.h diff --git a/README.md b/README.md index f2f9e1e..2fe5f32 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch. * [x] Image save * Extensions * [x] Draco mesh decoding(`TINYGLTF_ENABLE_DRACO` required) - * [x] KTX image support(`TINYGLTF_ENABLE_KTX` required) + * [x] KTX image support(no mipmap. `TINYGLTF_ENABLE_KTX` required) ## Examples @@ -83,6 +83,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch. * [ ] OpenEXR extension through TinyEXR. * [ ] 16bit PNG support in Serialization * [ ] Write example and tests for `animation` and `skin` +* [ ] mipmap support for KTX image. ## Licenses @@ -94,12 +95,15 @@ TinyGLTF uses the following third party libraries. * base64 : Copyright (C) 2004-2008 René Nyffenegger * stb_image.h : v2.08 - public domain image loader - [Github link](https://github.com/nothings/stb/blob/master/stb_image.h) * stb_image_write.h : v1.09 - public domain image writer - [Github link](https://github.com/nothings/stb/blob/master/stb_image_write.h) +* tinyktx.h : Copyright (c) 2019, DeanoC. Licensed under 2 clause BSD license. ## Build and example Copy `stb_image.h`, `stb_image_write.h`, `json.hpp` and `tiny_gltf.h` to your project. +If you enable KTX support(`TINYGLTF_ENABLE_KTX`), Copy `tinyktx.h` to your project. + ### Loading glTF 2.0 model ```c++ @@ -146,6 +150,7 @@ if (!ret) { * `TINYGLTF_NO_INCLUDE_STB_IMAGE `: Disable including `stb_image.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`. * `TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE `: Disable including `stb_image_write.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`. * `TINYGLTF_ENABLE_KTX` : Enable loading KTX images( https://www.khronos.org/opengles/sdk/tools/KTX/ ) using `tiny_ktx.h`. Supported MIME is `image/ktx` ( https://github.com/KhronosGroup/glTF/issues/835 ). See `models/Cube-KTX` for details. Application also defined `TINYKTX_IMPLEMENTATION` in **one** .cc file. + * `TINYGLTF_NO_INCLUDE_TINYKTX` : Disable including `tinyktx.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`. ### Saving gltTF 2.0 model @@ -184,9 +189,6 @@ $ ./tester $ ./tester_noexcept ``` -## Third party licenses +## Third party licenses used in unit tests -* json.hpp : Licensed under the MIT License . Copyright (c) 2013-2017 Niels Lohmann . -* stb_image : Public domain. * catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0. -* tiny_ktx : Copyright (c) 2019, DeanoC. Licensed under 2 clause BSD license. diff --git a/tiny_gltf.h b/tiny_gltf.h index 83ededc..888053e 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -492,14 +492,19 @@ struct Image { std::string name; int width; int height; - int component; + + // + // For KTX image, component, bits and pixel_type will be set to `0`, and KTX attributes are stored in `extras`. + // FIXME(syoyo): Find more proper way to handle KTX and non-KTX image by keeping backward-compatibility as much as possible. + // + int component; // The number of channel components in the image. int bits; // bit depth per channel. 8(byte), 16 or 32. int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually // UBYTE(bits = 8) or USHORT(bits = 16) std::vector image; int bufferView; // (required if no uri) std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", - // "image/bmp", "image/gif"] + // "image/bmp", "image/gif", etc] std::string uri; // (required if no mimeType) Value extras; ExtensionMap extensions; @@ -1124,6 +1129,12 @@ class TinyGLTF { #include "draco/core/decoder_buffer.h" #endif +#ifdef TINYGLTF_ENABLE_KTX +#ifndef TINYGLTF_NO_INCLUDE_TINY_KTX +#include "tiny_ktx.h" +#endif +#endif + #ifndef TINYGLTF_NO_STB_IMAGE #ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE #include "stb_image.h" @@ -1679,8 +1690,8 @@ void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) { load_image_user_data_ = user_data; } -#ifndef TINYGLTF_NO_STB_IMAGE -bool LoadImageData(Image *image, const int image_idx, std::string *err, +#if !defined(TINYGLTF_NO_STB_IMAGE) +bool LoadImageDataSTB(Image *image, const int image_idx, std::string *err, std::string *warn, int req_width, int req_height, const unsigned char *bytes, int size, void *user_data) { (void)user_data; @@ -1777,6 +1788,207 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, } #endif +#if defined(TINYGLTF_ENABLE_KTX) + +struct SimpleVFile { + std::string *err = nullptr; + const uint8_t *data = nullptr; + size_t pos = 0; + size_t len = 0; +}; + +static void tinyktxCallbackError(void *user, char const *msg) { + SimpleVFile *ss = reinterpret_cast(user); + + if (ss->err) { + (*ss->err) += "Tiny_Ktx ERROR: " + std::string(msg) + "\n"; + } +} + + +static void *tinyktxCallbackAlloc(void *user, size_t size) { + (void)user; + return malloc(size); +}; + +static void tinyktxCallbackFree(void *user, void *data) { + (void)user; + free(data); +}; + +static size_t tinyktxCallbackRead(void *user, void* data, size_t size) { + SimpleVFile *ss = reinterpret_cast(user); + + if ((ss->pos + size) >= ss->len) { + if (ss->err) { + std::stringstream msg; + msg << "KTX read: Invalid data length. pos = " << ss->pos << ", len = " << ss->len << ", requested size to read = " << size << "\n"; + (*ss->err) += msg.str(); + } + return size_t(0); + } + + memcpy(data, ss->data + ss->pos, size); + + return size; +}; + +static bool tinyktxCallbackSeek(void *user, int64_t offset) { + + SimpleVFile *ss = reinterpret_cast(user); + + if ((offset < 0) || (size_t(offset) >= ss->len)) { + if (ss->err) { + std::stringstream msg; + msg << "KTX read: Invalid data offset. len = " << ss->len << ", requested offset = " << offset << "\n"; + (*ss->err) += msg.str(); + } + + return false; + } + + ss->pos = offset; + return true; + +} + +static int64_t tinyktxCallbackTell(void *user) { + SimpleVFile *ss = reinterpret_cast(user); + + return int64_t(ss->pos); +}; + + +bool LoadImageDataKTX(Image *image, const int image_idx, std::string *err, + std::string *warn, int req_width, int req_height, + const unsigned char *bytes, int size, void *user_data) { + (void)user_data; + (void)warn; + (void)req_width; + (void)req_height; + + TinyKtx_Callbacks callbacks { + &tinyktxCallbackError, + &tinyktxCallbackAlloc, + &tinyktxCallbackFree, + tinyktxCallbackRead, + &tinyktxCallbackSeek, + &tinyktxCallbackTell + }; + + SimpleVFile vfile; + vfile.err = err; + vfile.pos = 0; + vfile.data = bytes; + vfile.len = size; + + auto ctx = TinyKtx_CreateContext( &callbacks, reinterpret_cast(&vfile) ); + TinyKtx_ReadHeader(ctx); + uint32_t w = TinyKtx_Width(ctx); + uint32_t h = TinyKtx_Height(ctx); + uint32_t d = TinyKtx_Depth(ctx); + + TinyKtx_Format fmt = TinyKtx_GetFormat(ctx); + + if(fmt == TKTX_UNDEFINED) { + TinyKtx_DestroyContext(ctx); + return false; + } + + int num_mipmaps = TinyKtx_NumberOfMipmaps(ctx); + if (num_mipmaps < 1) { + // ??? + if (err) { + (*err) += "Number of mipmaps are zero for KTX image[" + std::to_string(image_idx) + ".\n"; + } + TinyKtx_DestroyContext(ctx); + return false; + } + + { + // TODO(syoyo): Support mipmap. + int miplevel = 0; + + size_t byte_count = TinyKtx_ImageSize(ctx, miplevel); + if (byte_count == 0) { + // ??? + if (err) { + (*err) += "KTX image [" + std::to_string(image_idx) + "] has zero bytes.\n"; + } + + TinyKtx_DestroyContext(ctx); + return false; + } + + image->image.resize(byte_count); + memcpy(image->image.data(), TinyKtx_ImageRawData(ctx, miplevel), byte_count); + + TinyKtx_DestroyContext(ctx); + + // Set to 0 for KTX image. + image->pixel_type = 0; + image->bits = 0; + image->component = 0; + + // extras for KTX + Value::Object extras; + extras["ktx_format"] = Value(int(fmt)); // TODO(syoyo): Store OpenGL format + extras["depth"] = Value(int(d)); + extras["byteCount"] = Value(int(byte_count)); // Assume image size is < 2GB + image->extras = Value(extras); + // TODO(syoyo): Store more KTX attributes. + } + + image->width = w; + image->height = h; + + return true; +} +#endif + +bool LoadImageData(Image *image, const int image_idx, std::string *err, + std::string *warn, int req_width, int req_height, + const unsigned char *bytes, int size, void *user_data) { + + bool ret = false; + +#if defined(TINYGLTF_ENABLE_KTX) + // Try KTX image + + ret = tinygltf::LoadImageDataKTX(image, image_idx, err, warn, req_width, req_height, + bytes, size, user_data); + + if (ret) { + return true; + } + +#endif + +#if defined(TINYGLTF_NO_STB_IMAGE) + // Try to load images using stb_image(png, gif, bmp, tga, ...) + + ret = tinygltf::LoadImageDataSTB(image, image_idx, err, warn, req_width, req_height, + bytes, size, user_data); + + if (ret) { + return true; + } + +#endif + + (void)image; + (void)image_idx; + (void)err; + (void)warn; + (void)req_width; + (void)req_height; + (void)bytes; + (void)size; + (void)user_data; + + return false; +} + void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { WriteImageData = func; write_image_user_data_ = user_data; @@ -1800,6 +2012,8 @@ bool WriteImageData(const std::string *basepath, const std::string *filename, std::string header; std::vector data; + // TODO(LTE): Support ktx + if (ext == "png") { if ((image->bits != 8) || (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { @@ -2047,6 +2261,10 @@ static std::string MimeToExt(const std::string &mimeType) { return "bmp"; } else if (mimeType == "image/gif") { return "gif"; +#if defined(TINYGLTF_ENABLE_KTX) + } else if (mimeType == "image/ktx") { + return "ktx"; +#endif } return ""; @@ -2107,6 +2325,13 @@ bool IsDataURI(const std::string &in) { return true; } +#if defined(TINYGLTF_ENABLE_KTX) + header = "data:image/ktx;base64,"; + if (in.find(header) == 0) { + return true; + } +#endif + header = "data:text/plain;base64,"; if (in.find(header) == 0) { return true; @@ -2160,6 +2385,16 @@ bool DecodeDataURI(std::vector *out, std::string &mime_type, } } +#if defined(TINYGLTF_ENABLE_KTX) + if (data.empty()) { + header = "data:image/ktx;base64,"; + if (in.find(header) == 0) { + mime_type = "image/ktx"; + data = base64_decode(in.substr(header.size())); // cut mime string. + } + } +#endif + if (data.empty()) { header = "data:text/plain;base64,"; if (in.find(header) == 0) { diff --git a/tinyktx.h b/tinyktx.h new file mode 100644 index 0000000..027de9a --- /dev/null +++ b/tinyktx.h @@ -0,0 +1,1600 @@ +// BSD clause 2 license see full LICENSE text at end of file +#pragma once +#ifndef TINY_KTX_TINYKTX_H +#define TINY_KTX_TINYKTX_H + +#ifndef TINYKTX_HAVE_UINTXX_T +#include // for uint32_t and int64_t +#endif +#ifndef TINYKTX_HAVE_BOOL +#include // for bool +#endif +#ifndef TINYKTX_HAVE_SIZE_T +#include // for size_t +#endif +#ifndef TINYKTX_HAVE_MEMCPY +#include // for memcpy +#endif + +// if you don't want the helper format function or already have GL defines +// comment these two lines out +//#define TINYKTX_WANT_TINYKTX_FORMAT 0 +//#define TINYKTX_WANT_FAKE_GL_DEFINES 0 + + +#ifndef TINYKTX_WANT_TINYKTX_FORMAT +#define TINYKTX_WANT_TINYKTX_FORMAT 1 +#define TINYKTX_WANT_FAKE_GL_DEFINES 1 +#endif + +#ifndef TINYKTX_WANT_FAKE_GL_DEFINES +#define TINYKTX_WANT_FAKE_GL_DEFINES 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define TINYKTX_MAX_MIPMAPLEVELS 16 + +typedef struct TinyKtx_Context *TinyKtx_ContextHandle; + +typedef void *(*TinyKtx_AllocFunc)(void *user, size_t size); +typedef void (*TinyKtx_FreeFunc)(void *user, void *memory); +typedef size_t (*TinyKtx_ReadFunc)(void *user, void *buffer, size_t byteCount); +typedef bool (*TinyKtx_SeekFunc)(void *user, int64_t offset); +typedef int64_t (*TinyKtx_TellFunc)(void *user); +typedef void (*TinyKtx_ErrorFunc)(void *user, char const *msg); + +typedef struct TinyKtx_Callbacks { + TinyKtx_ErrorFunc error; + TinyKtx_AllocFunc alloc; + TinyKtx_FreeFunc free; + TinyKtx_ReadFunc read; + TinyKtx_SeekFunc seek; + TinyKtx_TellFunc tell; +} TinyKtx_Callbacks; + +TinyKtx_ContextHandle TinyKtx_CreateContext(TinyKtx_Callbacks const *callbacks, void *user); +void TinyKtx_DestroyContext(TinyKtx_ContextHandle handle); + +// reset like you reuse the context for another file (saves an alloc/free cycle) +void TinyKtx_Reset(TinyKtx_ContextHandle handle); + +// call this to read the header file should already be at the start of the KTX data +bool TinyKtx_ReadHeader(TinyKtx_ContextHandle handle); + +// this is slow linear search. TODO add iterator style reading of key value pairs +bool TinyKtx_GetValue(TinyKtx_ContextHandle handle, char const *key, void const **value); + +bool TinyKtx_Is1D(TinyKtx_ContextHandle handle); +bool TinyKtx_Is2D(TinyKtx_ContextHandle handle); +bool TinyKtx_Is3D(TinyKtx_ContextHandle handle); +bool TinyKtx_IsCubemap(TinyKtx_ContextHandle handle); +bool TinyKtx_IsArray(TinyKtx_ContextHandle handle); + +bool TinyKtx_Dimensions(TinyKtx_ContextHandle handle, uint32_t* width, uint32_t* height, uint32_t* depth, uint32_t* slices); +uint32_t TinyKtx_Width(TinyKtx_ContextHandle handle); +uint32_t TinyKtx_Height(TinyKtx_ContextHandle handle); +uint32_t TinyKtx_Depth(TinyKtx_ContextHandle handle); +uint32_t TinyKtx_ArraySlices(TinyKtx_ContextHandle handle); + +bool TinyKtx_GetFormatGL(TinyKtx_ContextHandle handle, uint32_t *glformat, uint32_t *gltype, uint32_t *glinternalformat, uint32_t* typesize, uint32_t* glbaseinternalformat); + +bool TinyKtx_NeedsGenerationOfMipmaps(TinyKtx_ContextHandle handle); +bool TinyKtx_NeedsEndianCorrecting(TinyKtx_ContextHandle handle); + +uint32_t TinyKtx_NumberOfMipmaps(TinyKtx_ContextHandle handle); +uint32_t TinyKtx_ImageSize(TinyKtx_ContextHandle handle, uint32_t mipmaplevel); + +// data return by ImageRawData is owned by the context. Don't free it! +void const *TinyKtx_ImageRawData(TinyKtx_ContextHandle handle, uint32_t mipmaplevel); + +typedef void (*TinyKtx_WriteFunc)(void *user, void const *buffer, size_t byteCount); + +typedef struct TinyKtx_WriteCallbacks { + TinyKtx_ErrorFunc error; + TinyKtx_AllocFunc alloc; + TinyKtx_FreeFunc free; + TinyKtx_WriteFunc write; +} TinyKtx_WriteCallbacks; + + +bool TinyKtx_WriteImageGL(TinyKtx_WriteCallbacks const *callbacks, + void *user, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t slices, + uint32_t mipmaplevels, + uint32_t format, + uint32_t internalFormat, + uint32_t baseFormat, + uint32_t type, + uint32_t typeSize, + bool cubemap, + uint32_t const *mipmapsizes, + void const **mipmaps); + +#if TINYKTX_WANT_TINYKTX_FORMAT != 0 +// ktx is based on GL (slightly confusing imho) texture format system +// there is format, internal format, type etc. + +// we try and expose a more dx12/vulkan/metal style of format +// but obviously still need to GL data so bare with me. +// a TinyKTX_Format is the equivilent to GL/KTX Format and Type +// the API doesn't expose the actual values (which come from GL itself) +// but provide an API call to crack them back into the actual GL values). + +// this may look very similar to Vulkan/Dx12 format (its not but related) +// these are even more closely related to my gfx_imageformat library... + +typedef enum TinyKtx_Format { + TKTX_UNDEFINED = 0, + TKTX_R4G4_UNORM_PACK8, + TKTX_R4G4B4A4_UNORM_PACK16, + TKTX_B4G4R4A4_UNORM_PACK16, + TKTX_R5G6B5_UNORM_PACK16, + TKTX_B5G6R5_UNORM_PACK16, + TKTX_R5G5B5A1_UNORM_PACK16, + TKTX_B5G5R5A1_UNORM_PACK16, + TKTX_A1R5G5B5_UNORM_PACK16, + + TKTX_R8_UNORM, + TKTX_R8_SNORM, + TKTX_R8_UINT, + TKTX_R8_SINT, + TKTX_R8_SRGB, + + TKTX_R8G8_UNORM, + TKTX_R8G8_SNORM, + TKTX_R8G8_UINT, + TKTX_R8G8_SINT, + TKTX_R8G8_SRGB, + + TKTX_R8G8B8_UNORM, + TKTX_R8G8B8_SNORM, + TKTX_R8G8B8_UINT, + TKTX_R8G8B8_SINT, + TKTX_R8G8B8_SRGB, + TKTX_B8G8R8_UNORM, + TKTX_B8G8R8_SNORM, + TKTX_B8G8R8_UINT, + TKTX_B8G8R8_SINT, + TKTX_B8G8R8_SRGB, + + TKTX_R8G8B8A8_UNORM, + TKTX_R8G8B8A8_SNORM, + TKTX_R8G8B8A8_UINT, + TKTX_R8G8B8A8_SINT, + TKTX_R8G8B8A8_SRGB, + TKTX_B8G8R8A8_UNORM, + TKTX_B8G8R8A8_SNORM, + TKTX_B8G8R8A8_UINT, + TKTX_B8G8R8A8_SINT, + TKTX_B8G8R8A8_SRGB, + + TKTX_A8B8G8R8_UNORM_PACK32, + TKTX_A8B8G8R8_SNORM_PACK32, + TKTX_A8B8G8R8_UINT_PACK32, + TKTX_A8B8G8R8_SINT_PACK32, + TKTX_A8B8G8R8_SRGB_PACK32, + + TKTX_E5B9G9R9_UFLOAT_PACK32, + TKTX_A2R10G10B10_UNORM_PACK32, + TKTX_A2R10G10B10_UINT_PACK32, + TKTX_A2B10G10R10_UNORM_PACK32, + TKTX_A2B10G10R10_UINT_PACK32, + TKTX_B10G11R11_UFLOAT_PACK32, + + TKTX_R16_UNORM, + TKTX_R16_SNORM, + TKTX_R16_UINT, + TKTX_R16_SINT, + TKTX_R16_SFLOAT, + TKTX_R16G16_UNORM, + TKTX_R16G16_SNORM, + TKTX_R16G16_UINT, + TKTX_R16G16_SINT, + TKTX_R16G16_SFLOAT, + TKTX_R16G16B16_UNORM, + TKTX_R16G16B16_SNORM, + TKTX_R16G16B16_UINT, + TKTX_R16G16B16_SINT, + TKTX_R16G16B16_SFLOAT, + TKTX_R16G16B16A16_UNORM, + TKTX_R16G16B16A16_SNORM, + TKTX_R16G16B16A16_UINT, + TKTX_R16G16B16A16_SINT, + TKTX_R16G16B16A16_SFLOAT, + TKTX_R32_UINT, + TKTX_R32_SINT, + TKTX_R32_SFLOAT, + TKTX_R32G32_UINT, + TKTX_R32G32_SINT, + TKTX_R32G32_SFLOAT, + TKTX_R32G32B32_UINT, + TKTX_R32G32B32_SINT, + TKTX_R32G32B32_SFLOAT, + TKTX_R32G32B32A32_UINT, + TKTX_R32G32B32A32_SINT, + TKTX_R32G32B32A32_SFLOAT, + + TKTX_BC1_RGB_UNORM_BLOCK, + TKTX_BC1_RGB_SRGB_BLOCK, + TKTX_BC1_RGBA_UNORM_BLOCK, + TKTX_BC1_RGBA_SRGB_BLOCK, + TKTX_BC2_UNORM_BLOCK, + TKTX_BC2_SRGB_BLOCK, + TKTX_BC3_UNORM_BLOCK, + TKTX_BC3_SRGB_BLOCK, + TKTX_BC4_UNORM_BLOCK, + TKTX_BC4_SNORM_BLOCK, + TKTX_BC5_UNORM_BLOCK, + TKTX_BC5_SNORM_BLOCK, + TKTX_BC6H_UFLOAT_BLOCK, + TKTX_BC6H_SFLOAT_BLOCK, + TKTX_BC7_UNORM_BLOCK, + TKTX_BC7_SRGB_BLOCK, + + TKTX_ETC2_R8G8B8_UNORM_BLOCK, + TKTX_ETC2_R8G8B8A1_UNORM_BLOCK, + TKTX_ETC2_R8G8B8A8_UNORM_BLOCK, + TKTX_ETC2_R8G8B8_SRGB_BLOCK, + TKTX_ETC2_R8G8B8A1_SRGB_BLOCK, + TKTX_ETC2_R8G8B8A8_SRGB_BLOCK, + TKTX_EAC_R11_UNORM_BLOCK, + TKTX_EAC_R11G11_UNORM_BLOCK, + TKTX_EAC_R11_SNORM_BLOCK, + TKTX_EAC_R11G11_SNORM_BLOCK, + + TKTX_PVR_2BPP_BLOCK, + TKTX_PVR_2BPPA_BLOCK, + TKTX_PVR_4BPP_BLOCK, + TKTX_PVR_4BPPA_BLOCK, + TKTX_PVR_2BPP_SRGB_BLOCK, + TKTX_PVR_2BPPA_SRGB_BLOCK, + TKTX_PVR_4BPP_SRGB_BLOCK, + TKTX_PVR_4BPPA_SRGB_BLOCK, + + TKTX_ASTC_4x4_UNORM_BLOCK, + TKTX_ASTC_4x4_SRGB_BLOCK, + TKTX_ASTC_5x4_UNORM_BLOCK, + TKTX_ASTC_5x4_SRGB_BLOCK, + TKTX_ASTC_5x5_UNORM_BLOCK, + TKTX_ASTC_5x5_SRGB_BLOCK, + TKTX_ASTC_6x5_UNORM_BLOCK, + TKTX_ASTC_6x5_SRGB_BLOCK, + TKTX_ASTC_6x6_UNORM_BLOCK, + TKTX_ASTC_6x6_SRGB_BLOCK, + TKTX_ASTC_8x5_UNORM_BLOCK, + TKTX_ASTC_8x5_SRGB_BLOCK, + TKTX_ASTC_8x6_UNORM_BLOCK, + TKTX_ASTC_8x6_SRGB_BLOCK, + TKTX_ASTC_8x8_UNORM_BLOCK, + TKTX_ASTC_8x8_SRGB_BLOCK, + TKTX_ASTC_10x5_UNORM_BLOCK, + TKTX_ASTC_10x5_SRGB_BLOCK, + TKTX_ASTC_10x6_UNORM_BLOCK, + TKTX_ASTC_10x6_SRGB_BLOCK, + TKTX_ASTC_10x8_UNORM_BLOCK, + TKTX_ASTC_10x8_SRGB_BLOCK, + TKTX_ASTC_10x10_UNORM_BLOCK, + TKTX_ASTC_10x10_SRGB_BLOCK, + TKTX_ASTC_12x10_UNORM_BLOCK, + TKTX_ASTC_12x10_SRGB_BLOCK, + TKTX_ASTC_12x12_UNORM_BLOCK, + TKTX_ASTC_12x12_SRGB_BLOCK, + +} TinyKtx_Format; + +TinyKtx_Format TinyKtx_GetFormat(TinyKtx_ContextHandle handle); +bool TinyKtx_CrackFormatToGL(TinyKtx_Format format, uint32_t *glformat, uint32_t *gltype, uint32_t *glinternalformat, uint32_t* typesize); +bool TinyKtx_WriteImage(TinyKtx_WriteCallbacks const *callbacks, + void *user, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t slices, + uint32_t mipmaplevels, + TinyKtx_Format format, + bool cubemap, + uint32_t const *mipmapsizes, + void const **mipmaps); +#endif + +#if TINYKTX_WANT_FAKE_GL_DEFINES != 0 +// GL types +#define TINYKTX_GL_TYPE_COMPRESSED 0x0 +#define TINYKTX_GL_TYPE_BYTE 0x1400 +#define TINYKTX_GL_TYPE_UNSIGNED_BYTE 0x1401 +#define TINYKTX_GL_TYPE_SHORT 0x1402 +#define TINYKTX_GL_TYPE_UNSIGNED_SHORT 0x1403 +#define TINYKTX_GL_TYPE_INT 0x1404 +#define TINYKTX_GL_TYPE_UNSIGNED_INT 0x1405 +#define TINYKTX_GL_TYPE_FLOAT 0x1406 +#define TINYKTX_GL_TYPE_DOUBLE 0x140A +#define TINYKTX_GL_TYPE_HALF_FLOAT 0x140B +#define TINYKTX_GL_TYPE_UNSIGNED_BYTE_3_3_2 0x8032 +#define TINYKTX_GL_TYPE_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define TINYKTX_GL_TYPE_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define TINYKTX_GL_TYPE_UNSIGNED_INT_8_8_8_8 0x8035 +#define TINYKTX_GL_TYPE_UNSIGNED_INT_10_10_10_2 0x8036 +#define TINYKTX_GL_TYPE_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define TINYKTX_GL_TYPE_UNSIGNED_SHORT_5_6_5 0x8363 +#define TINYKTX_GL_TYPE_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define TINYKTX_GL_TYPE_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define TINYKTX_GL_TYPE_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define TINYKTX_GL_TYPE_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define TINYKTX_GL_TYPE_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define TINYKTX_GL_TYPE_UNSIGNED_INT_24_8 0x84FA +#define TINYKTX_GL_TYPE_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define TINYKTX_GL_TYPE_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define TINYKTX_GL_TYPE_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD + +// formats +#define TINYKTX_GL_FORMAT_RED 0x1903 +#define TINYKTX_GL_FORMAT_GREEN 0x1904 +#define TINYKTX_GL_FORMAT_BLUE 0x1905 +#define TINYKTX_GL_FORMAT_ALPHA 0x1906 +#define TINYKTX_GL_FORMAT_RGB 0x1907 +#define TINYKTX_GL_FORMAT_RGBA 0x1908 +#define TINYKTX_GL_FORMAT_LUMINANCE 0x1909 +#define TINYKTX_GL_FORMAT_LUMINANCE_ALPHA 0x190A +#define TINYKTX_GL_FORMAT_ABGR 0x8000 +#define TINYKTX_GL_FORMAT_INTENSITY 0x8049 +#define TINYKTX_GL_FORMAT_BGR 0x80E0 +#define TINYKTX_GL_FORMAT_BGRA 0x80E1 +#define TINYKTX_GL_FORMAT_RG 0x8227 +#define TINYKTX_GL_FORMAT_RG_INTEGER 0x8228 +#define TINYKTX_GL_FORMAT_SRGB 0x8C40 +#define TINYKTX_GL_FORMAT_SRGB_ALPHA 0x8C42 +#define TINYKTX_GL_FORMAT_SLUMINANCE_ALPHA 0x8C44 +#define TINYKTX_GL_FORMAT_SLUMINANCE 0x8C46 +#define TINYKTX_GL_FORMAT_RED_INTEGER 0x8D94 +#define TINYKTX_GL_FORMAT_GREEN_INTEGER 0x8D95 +#define TINYKTX_GL_FORMAT_BLUE_INTEGER 0x8D96 +#define TINYKTX_GL_FORMAT_ALPHA_INTEGER 0x8D97 +#define TINYKTX_GL_FORMAT_RGB_INTEGER 0x8D98 +#define TINYKTX_GL_FORMAT_RGBA_INTEGER 0x8D99 +#define TINYKTX_GL_FORMAT_BGR_INTEGER 0x8D9A +#define TINYKTX_GL_FORMAT_BGRA_INTEGER 0x8D9B +#define TINYKTX_GL_FORMAT_RED_SNORM 0x8F90 +#define TINYKTX_GL_FORMAT_RG_SNORM 0x8F91 +#define TINYKTX_GL_FORMAT_RGB_SNORM 0x8F92 +#define TINYKTX_GL_FORMAT_RGBA_SNORM 0x8F93 + +#define TINYKTX_GL_INTFORMAT_ALPHA4 0x803B +#define TINYKTX_GL_INTFORMAT_ALPHA8 0x803C +#define TINYKTX_GL_INTFORMAT_ALPHA12 0x803D +#define TINYKTX_GL_INTFORMAT_ALPHA16 0x803E +#define TINYKTX_GL_INTFORMAT_LUMINANCE4 0x803F +#define TINYKTX_GL_INTFORMAT_LUMINANCE8 0x8040 +#define TINYKTX_GL_INTFORMAT_LUMINANCE12 0x8041 +#define TINYKTX_GL_INTFORMAT_LUMINANCE16 0x8042 +#define TINYKTX_GL_INTFORMAT_LUMINANCE4_ALPHA4 0x8043 +#define TINYKTX_GL_INTFORMAT_LUMINANCE6_ALPHA2 0x8044 +#define TINYKTX_GL_INTFORMAT_LUMINANCE8_ALPHA8 0x8045 +#define TINYKTX_GL_INTFORMAT_LUMINANCE12_ALPHA4 0x8046 +#define TINYKTX_GL_INTFORMAT_LUMINANCE12_ALPHA12 0x8047 +#define TINYKTX_GL_INTFORMAT_LUMINANCE16_ALPHA16 0x8048 +#define TINYKTX_GL_INTFORMAT_INTENSITY4 0x804A +#define TINYKTX_GL_INTFORMAT_INTENSITY8 0x804B +#define TINYKTX_GL_INTFORMAT_INTENSITY12 0x804C +#define TINYKTX_GL_INTFORMAT_INTENSITY16 0x804D +#define TINYKTX_GL_INTFORMAT_RGB2 0x804E +#define TINYKTX_GL_INTFORMAT_RGB4 0x804F +#define TINYKTX_GL_INTFORMAT_RGB5 0x8050 +#define TINYKTX_GL_INTFORMAT_RGB8 0x8051 +#define TINYKTX_GL_INTFORMAT_RGB10 0x8052 +#define TINYKTX_GL_INTFORMAT_RGB12 0x8053 +#define TINYKTX_GL_INTFORMAT_RGB16 0x8054 +#define TINYKTX_GL_INTFORMAT_RGBA2 0x8055 +#define TINYKTX_GL_INTFORMAT_RGBA4 0x8056 +#define TINYKTX_GL_INTFORMAT_RGB5_A1 0x8057 +#define TINYKTX_GL_INTFORMAT_RGBA8 0x8058 +#define TINYKTX_GL_INTFORMAT_RGB10_A2 0x8059 +#define TINYKTX_GL_INTFORMAT_RGBA12 0x805A +#define TINYKTX_GL_INTFORMAT_RGBA16 0x805B +#define TINYKTX_GL_INTFORMAT_R8 0x8229 +#define TINYKTX_GL_INTFORMAT_R16 0x822A +#define TINYKTX_GL_INTFORMAT_RG8 0x822B +#define TINYKTX_GL_INTFORMAT_RG16 0x822C +#define TINYKTX_GL_INTFORMAT_R16F 0x822D +#define TINYKTX_GL_INTFORMAT_R32F 0x822E +#define TINYKTX_GL_INTFORMAT_RG16F 0x822F +#define TINYKTX_GL_INTFORMAT_RG32F 0x8230 +#define TINYKTX_GL_INTFORMAT_R8I 0x8231 +#define TINYKTX_GL_INTFORMAT_R8UI 0x8232 +#define TINYKTX_GL_INTFORMAT_R16I 0x8233 +#define TINYKTX_GL_INTFORMAT_R16UI 0x8234 +#define TINYKTX_GL_INTFORMAT_R32I 0x8235 +#define TINYKTX_GL_INTFORMAT_R32UI 0x8236 +#define TINYKTX_GL_INTFORMAT_RG8I 0x8237 +#define TINYKTX_GL_INTFORMAT_RG8UI 0x8238 +#define TINYKTX_GL_INTFORMAT_RG16I 0x8239 +#define TINYKTX_GL_INTFORMAT_RG16UI 0x823A +#define TINYKTX_GL_INTFORMAT_RG32I 0x823B +#define TINYKTX_GL_INTFORMAT_RG32UI 0x823C +#define TINYKTX_GL_INTFORMAT_RGBA32F 0x8814 +#define TINYKTX_GL_INTFORMAT_RGB32F 0x8815 +#define TINYKTX_GL_INTFORMAT_RGBA16F 0x881A +#define TINYKTX_GL_INTFORMAT_RGB16F 0x881B +#define TINYKTX_GL_INTFORMAT_R11F_G11F_B10F 0x8C3A +#define TINYKTX_GL_INTFORMAT_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define TINYKTX_GL_INTFORMAT_RGB9_E5 0x8C3D +#define TINYKTX_GL_INTFORMAT_SRGB8 0x8C41 +#define TINYKTX_GL_INTFORMAT_SRGB8_ALPHA8 0x8C43 +#define TINYKTX_GL_INTFORMAT_SLUMINANCE8_ALPHA8 0x8C45 +#define TINYKTX_GL_INTFORMAT_SLUMINANCE8 0x8C47 +#define TINYKTX_GL_INTFORMAT_RGB565 0x8D62 +#define TINYKTX_GL_INTFORMAT_RGBA32UI 0x8D70 +#define TINYKTX_GL_INTFORMAT_RGB32UI 0x8D71 +#define TINYKTX_GL_INTFORMAT_RGBA16UI 0x8D76 +#define TINYKTX_GL_INTFORMAT_RGB16UI 0x8D77 +#define TINYKTX_GL_INTFORMAT_RGBA8UI 0x8D7C +#define TINYKTX_GL_INTFORMAT_RGB8UI 0x8D7D +#define TINYKTX_GL_INTFORMAT_RGBA32I 0x8D82 +#define TINYKTX_GL_INTFORMAT_RGB32I 0x8D83 +#define TINYKTX_GL_INTFORMAT_RGBA16I 0x8D88 +#define TINYKTX_GL_INTFORMAT_RGB16I 0x8D89 +#define TINYKTX_GL_INTFORMAT_RGBA8I 0x8D8E +#define TINYKTX_GL_INTFORMAT_RGB8I 0x8D8F +#define TINYKTX_GL_INTFORMAT_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define TINYKTX_GL_INTFORMAT_R8_SNORM 0x8F94 +#define TINYKTX_GL_INTFORMAT_RG8_SNORM 0x8F95 +#define TINYKTX_GL_INTFORMAT_RGB8_SNORM 0x8F96 +#define TINYKTX_GL_INTFORMAT_RGBA8_SNORM 0x8F97 +#define TINYKTX_GL_INTFORMAT_R16_SNORM 0x8F98 +#define TINYKTX_GL_INTFORMAT_RG16_SNORM 0x8F99 +#define TINYKTX_GL_INTFORMAT_RGB16_SNORM 0x8F9A +#define TINYKTX_GL_INTFORMAT_RGBA16_SNORM 0x8F9B +#define TINYKTX_GL_INTFORMAT_ALPHA8_SNORM 0x9014 +#define TINYKTX_GL_INTFORMAT_LUMINANCE8_SNORM 0x9015 +#define TINYKTX_GL_INTFORMAT_LUMINANCE8_ALPHA8_SNORM 0x9016 +#define TINYKTX_GL_INTFORMAT_INTENSITY8_SNORM 0x9017 +#define TINYKTX_GL_INTFORMAT_ALPHA16_SNORM 0x9018 +#define TINYKTX_GL_INTFORMAT_LUMINANCE16_SNORM 0x9019 +#define TINYKTX_GL_INTFORMAT_LUMINANCE16_ALPHA16_SNORM 0x901A +#define TINYKTX_GL_INTFORMAT_INTENSITY16_SNORM 0x901B + +#define TINYKTX_GL_PALETTE4_RGB8_OES 0x8B90 +#define TINYKTX_GL_PALETTE4_RGBA8_OES 0x8B91 +#define TINYKTX_GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define TINYKTX_GL_PALETTE4_RGBA4_OES 0x8B93 +#define TINYKTX_GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define TINYKTX_GL_PALETTE8_RGB8_OES 0x8B95 +#define TINYKTX_GL_PALETTE8_RGBA8_OES 0x8B96 +#define TINYKTX_GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define TINYKTX_GL_PALETTE8_RGBA4_OES 0x8B98 +#define TINYKTX_GL_PALETTE8_RGB5_A1_OES 0x8B99 + +// compressed formats + +#define TINYKTX_GL_COMPRESSED_RGB_S3TC_DXT1 0x83F0 +#define TINYKTX_GL_COMPRESSED_RGBA_S3TC_DXT1 0x83F1 +#define TINYKTX_GL_COMPRESSED_RGBA_S3TC_DXT3 0x83F2 +#define TINYKTX_GL_COMPRESSED_RGBA_S3TC_DXT5 0x83F3 +#define TINYKTX_GL_COMPRESSED_3DC_X_AMD 0x87F9 +#define TINYKTX_GL_COMPRESSED_3DC_XY_AMD 0x87FA +#define TINYKTX_GL_COMPRESSED_ATC_RGBA_INTERPOLATED_ALPHA 0x87EE +#define TINYKTX_GL_COMPRESSED_SRGB_PVRTC_2BPPV1 0x8A54 +#define TINYKTX_GL_COMPRESSED_SRGB_PVRTC_4BPPV1 0x8A55 +#define TINYKTX_GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1 0x8A56 +#define TINYKTX_GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1 0x8A57 +#define TINYKTX_GL_COMPRESSED_RGB_PVRTC_4BPPV1 0x8C00 +#define TINYKTX_GL_COMPRESSED_RGB_PVRTC_2BPPV1 0x8C01 +#define TINYKTX_GL_COMPRESSED_RGBA_PVRTC_4BPPV1 0x8C02 +#define TINYKTX_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 0x8C03 +#define TINYKTX_GL_COMPRESSED_SRGB_S3TC_DXT1 0x8C4C +#define TINYKTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1 0x8C4D +#define TINYKTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3 0x8C4E +#define TINYKTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5 0x8C4F +#define TINYKTX_GL_COMPRESSED_LUMINANCE_LATC1 0x8C70 +#define TINYKTX_GL_COMPRESSED_SIGNED_LUMINANCE_LATC1 0x8C71 +#define TINYKTX_GL_COMPRESSED_LUMINANCE_ALPHA_LATC2 0x8C72 +#define TINYKTX_GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2 0x8C73 +#define TINYKTX_GL_COMPRESSED_ATC_RGB 0x8C92 +#define TINYKTX_GL_COMPRESSED_ATC_RGBA_EXPLICIT_ALPHA 0x8C93 +#define TINYKTX_GL_COMPRESSED_RED_RGTC1 0x8DBB +#define TINYKTX_GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define TINYKTX_GL_COMPRESSED_RED_GREEN_RGTC2 0x8DBD +#define TINYKTX_GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 0x8DBE +#define TINYKTX_GL_COMPRESSED_ETC1_RGB8_OES 0x8D64 +#define TINYKTX_GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define TINYKTX_GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define TINYKTX_GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define TINYKTX_GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define TINYKTX_GL_COMPRESSED_R11_EAC 0x9270 +#define TINYKTX_GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define TINYKTX_GL_COMPRESSED_RG11_EAC 0x9272 +#define TINYKTX_GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define TINYKTX_GL_COMPRESSED_RGB8_ETC2 0x9274 +#define TINYKTX_GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define TINYKTX_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define TINYKTX_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define TINYKTX_GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define TINYKTX_GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2 0x93F0 +#define TINYKTX_GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2 0x93F1 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_4x4 0x93B0 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_5x4 0x93B1 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_5x5 0x93B2 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_6x5 0x93B3 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_6x6 0x93B4 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_8x5 0x93B5 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_8x6 0x93B6 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_8x8 0x93B7 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_10x5 0x93B8 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_10x6 0x93B9 +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_10x8 0x93BA +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_10x10 0x93BB +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_12x10 0x93BC +#define TINYKTX_GL_COMPRESSED_RGBA_ASTC_12x12 0x93BD +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4 0x93D0 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4 0x93D1 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5 0x93D2 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5 0x93D3 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6 0x93D4 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5 0x93D5 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6 0x93D6 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8 0x93D7 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5 0x93D8 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6 0x93D9 +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8 0x93DA +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10 0x93DB +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10 0x93DC +#define TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12 0x93DD + +#endif + +#ifdef TINYKTX_IMPLEMENTATION + +typedef struct TinyKtx_Header { + uint8_t identifier[12]; + uint32_t endianness; + uint32_t glType; + uint32_t glTypeSize; + uint32_t glFormat; + uint32_t glInternalFormat; + uint32_t glBaseInternalFormat; + uint32_t pixelWidth; + uint32_t pixelHeight; + uint32_t pixelDepth; + uint32_t numberOfArrayElements; + uint32_t numberOfFaces; + uint32_t numberOfMipmapLevels; + uint32_t bytesOfKeyValueData; + +} TinyKtx_Header; + +typedef struct TinyKtx_KeyValuePair { + uint32_t size; +} TinyKtx_KeyValuePair; // followed by at least size bytes (aligned to 4) + + +typedef struct TinyKtx_Context { + TinyKtx_Callbacks callbacks; + void *user; + uint64_t headerPos; + uint64_t firstImagePos; + + TinyKtx_Header header; + TinyKtx_KeyValuePair const *keyData; + bool headerValid; + bool sameEndian; + + uint32_t mipMapSizes[TINYKTX_MAX_MIPMAPLEVELS]; + uint8_t const *mipmaps[TINYKTX_MAX_MIPMAPLEVELS]; + +} TinyKtx_Context; + +static uint8_t TinyKtx_fileIdentifier[12] = { + 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A +}; +static void TinyKtx_NullErrorFunc(void *user, char const *msg) {} + +TinyKtx_ContextHandle TinyKtx_CreateContext(TinyKtx_Callbacks const *callbacks, void *user) { + TinyKtx_Context *ctx = (TinyKtx_Context *) callbacks->alloc(user, sizeof(TinyKtx_Context)); + if (ctx == NULL) + return NULL; + + memset(ctx, 0, sizeof(TinyKtx_Context)); + memcpy(&ctx->callbacks, callbacks, sizeof(TinyKtx_Callbacks)); + ctx->user = user; + if (ctx->callbacks.error == NULL) { + ctx->callbacks.error = &TinyKtx_NullErrorFunc; + } + + if (ctx->callbacks.read == NULL) { + ctx->callbacks.error(user, "TinyKtx must have read callback"); + return NULL; + } + if (ctx->callbacks.alloc == NULL) { + ctx->callbacks.error(user, "TinyKtx must have alloc callback"); + return NULL; + } + if (ctx->callbacks.free == NULL) { + ctx->callbacks.error(user, "TinyKtx must have free callback"); + return NULL; + } + if (ctx->callbacks.seek == NULL) { + ctx->callbacks.error(user, "TinyKtx must have seek callback"); + return NULL; + } + if (ctx->callbacks.tell == NULL) { + ctx->callbacks.error(user, "TinyKtx must have tell callback"); + return NULL; + } + + TinyKtx_Reset(ctx); + + return ctx; +} + +void TinyKtx_DestroyContext(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return; + TinyKtx_Reset(handle); + ctx->callbacks.free(ctx->user, ctx); +} + +void TinyKtx_Reset(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return; + + // backup user provided callbacks and data + TinyKtx_Callbacks callbacks; + memcpy(&callbacks, &ctx->callbacks, sizeof(TinyKtx_Callbacks)); + void *user = ctx->user; + + // free memory of sub data + if (ctx->keyData != NULL) { + ctx->callbacks.free(ctx->user, (void *) ctx->keyData); + } + for (int i = 0; i < TINYKTX_MAX_MIPMAPLEVELS; ++i) { + if (ctx->mipmaps[i] != NULL) { + ctx->callbacks.free(ctx->user, (void *) ctx->mipmaps[i]); + } + } + + // reset to default state + memset(ctx, 0, sizeof(TinyKtx_Context)); + memcpy(&ctx->callbacks, &callbacks, sizeof(TinyKtx_Callbacks)); + ctx->user = user; + +} + +bool TinyKtx_ReadHeader(TinyKtx_ContextHandle handle) { + + static uint32_t const sameEndianDecider = 0x04030201; + static uint32_t const differentEndianDecider = 0x01020304; + + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + + ctx->headerPos = ctx->callbacks.tell(ctx->user); + ctx->callbacks.read(ctx->user, &ctx->header, sizeof(TinyKtx_Header)); + + if (memcmp(&ctx->header.identifier, TinyKtx_fileIdentifier, 12) != 0) { + ctx->callbacks.error(ctx->user, "Not a KTX file or corrupted as identified isn't valid"); + return false; + } + + if (ctx->header.endianness == sameEndianDecider) { + ctx->sameEndian = true; + } else if (ctx->header.endianness == differentEndianDecider) { + ctx->sameEndian = false; + } else { + // corrupt or mid endian? + ctx->callbacks.error(ctx->user, "Endian Error"); + return false; + } + + if (ctx->header.numberOfFaces != 1 && ctx->header.numberOfFaces != 6) { + ctx->callbacks.error(ctx->user, "no. of Faces must be 1 or 6"); + return false; + } + + ctx->keyData = (TinyKtx_KeyValuePair const *) ctx->callbacks.alloc(ctx->user, ctx->header.bytesOfKeyValueData); + ctx->callbacks.read(ctx->user, (void *) ctx->keyData, ctx->header.bytesOfKeyValueData); + + ctx->firstImagePos = ctx->callbacks.tell(ctx->user); + + ctx->headerValid = true; + return true; +} +bool TinyKtx_GetValue(TinyKtx_ContextHandle handle, char const *key, void const **value) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + + if (ctx->keyData == NULL) { + ctx->callbacks.error(ctx->user, "No key value data in this KTX"); + return false; + } + + TinyKtx_KeyValuePair const *curKey = ctx->keyData; + while (((uint8_t *) curKey - (uint8_t *) ctx->keyData) < ctx->header.bytesOfKeyValueData) { + char const *kvp = (char const *) curKey; + + if (strcmp(kvp, key) == 0) { + size_t sl = strlen(kvp); + *value = (void const *) (kvp + sl); + return true; + } + curKey = curKey + ((curKey->size + 3u) & ~3u); + } + return false; +} + +bool TinyKtx_Is1D(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + + return (ctx->header.pixelHeight == 1 && ctx->header.pixelDepth == 1); +} +bool TinyKtx_Is2D(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + + return (ctx->header.pixelHeight != 1 && ctx->header.pixelDepth == 1); +} +bool TinyKtx_Is3D(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + + return (ctx->header.pixelHeight != 1 && ctx->header.pixelDepth != 1); +} + +bool TinyKtx_IsCubemap(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + + return (ctx->header.numberOfFaces == 6); +} +bool TinyKtx_IsArray(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + + return (ctx->header.numberOfArrayElements > 1); +} +bool TinyKtx_Dimensions(TinyKtx_ContextHandle handle, + uint32_t *width, + uint32_t *height, + uint32_t *depth, + uint32_t *slices) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + if (width) + *width = ctx->header.pixelWidth; + if (height) + *height = ctx->header.pixelWidth; + if (depth) + *depth = ctx->header.pixelDepth; + if (slices) + *slices = ctx->header.numberOfArrayElements; + + return true; +} + +uint32_t TinyKtx_Width(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return 0; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return 0; + } + + return ctx->header.pixelWidth; +} + +uint32_t TinyKtx_Height(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return 0; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return 0; + } + + return ctx->header.pixelHeight; +} + +uint32_t TinyKtx_Depth(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return 0; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return 0; + } + + return ctx->header.pixelDepth; +} + +uint32_t TinyKtx_ArraySlices(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return 0; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return 0; + } + + return ctx->header.numberOfArrayElements; +} + +uint32_t TinyKtx_NumberOfMipmaps(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return 0; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return 0; + } + + return ctx->header.numberOfMipmapLevels ? ctx->header.numberOfMipmapLevels : 1; +} + +bool TinyKtx_NeedsGenerationOfMipmaps(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + + return ctx->header.numberOfMipmapLevels == 0; +} + +bool TinyKtx_NeedsEndianCorrecting(TinyKtx_ContextHandle handle) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + + return ctx->sameEndian == false; +} + +bool TinyKtx_GetFormatGL(TinyKtx_ContextHandle handle, uint32_t *glformat, uint32_t *gltype, uint32_t *glinternalformat, uint32_t* typesize, uint32_t* glbaseinternalformat) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return false; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return false; + } + + *glformat = ctx->header.glFormat; + *gltype = ctx->header.glType; + *glinternalformat = ctx->header.glInternalFormat; + *glbaseinternalformat = ctx->header.glBaseInternalFormat; + *typesize = ctx->header.glBaseInternalFormat; + + return true; +} + +bool TinyKtx_WriteImageGL(TinyKtx_WriteCallbacks const *callbacks, + void *user, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t slices, + uint32_t mipmaplevels, + uint32_t format, + uint32_t internalFormat, + uint32_t baseFormat, + uint32_t type, + uint32_t typeSize, + bool cubemap, + uint32_t const *mipmapsizes, + void const **mipmaps) { + + TinyKtx_Header header; + memcpy(header.identifier, TinyKtx_fileIdentifier, 12); + header.endianness = 0x04030201; + header.glFormat = format; + header.glInternalFormat = internalFormat; + header.glBaseInternalFormat = baseFormat; + header.glType = type; + header.glTypeSize = typeSize; + header.pixelWidth = width; + header.pixelHeight = height; + header.pixelDepth = depth; + header.numberOfArrayElements = slices; + header.numberOfFaces = cubemap ? 6 : 1; + header.numberOfMipmapLevels = mipmaplevels; + // TODO keyvalue pair data + header.bytesOfKeyValueData = 0; + callbacks->write(user, &header, sizeof(TinyKtx_Header)); + + static uint8_t const padding[4] = {0, 0, 0, 0}; + + // TODO this might be wrong for non array cubemaps with < 4 bytes per pixel... + // cubemap padding needs factoring in. + for (uint32_t i = 0u; i < mipmaplevels; ++i) { + callbacks->write(user, mipmapsizes + i, sizeof(uint32_t)); + callbacks->write(user, mipmaps[i], mipmapsizes[i]); + callbacks->write(user, padding, ((mipmapsizes[i] + 3u) & ~3u) - mipmapsizes[i]); + } + + return true; +} + +static uint32_t imageSize(TinyKtx_ContextHandle handle, uint32_t mipmaplevel, bool seekLast) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return 0; + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return 0; + } + if (mipmaplevel >= ctx->header.numberOfMipmapLevels) { + ctx->callbacks.error(ctx->user, "Invalid mipmap level"); + return 0; + } + if (mipmaplevel >= TINYKTX_MAX_MIPMAPLEVELS) { + ctx->callbacks.error(ctx->user, "Invalid mipmap level"); + return 0; + } + if (!seekLast && ctx->mipMapSizes[mipmaplevel] != 0) + return ctx->mipMapSizes[mipmaplevel]; + + uint64_t currentOffset = ctx->firstImagePos; + for (uint32_t i = 0; i <= mipmaplevel; ++i) { + uint32_t size; + // if we have already read this mipmaps size use it + if (ctx->mipMapSizes[i] != 0) { + size = ctx->mipMapSizes[i]; + if (seekLast && i == mipmaplevel) { + ctx->callbacks.seek(ctx->user, currentOffset + sizeof(uint32_t)); + } + } else { + // otherwise seek and read it + ctx->callbacks.seek(ctx->user, currentOffset); + size_t readchk = ctx->callbacks.read(ctx->user, &size, sizeof(uint32_t)); + if(readchk != 4) { + ctx->callbacks.error(ctx->user, "Reading image size error"); + return 0; + } + + if (ctx->header.numberOfFaces == 6 && ctx->header.numberOfArrayElements == 0) { + size = ((size + 3u) & ~3u) * 6; // face padding and 6 faces + } + + ctx->mipMapSizes[i] = size; + } + currentOffset += (size + sizeof(uint32_t) + 3u) & ~3u; // size + mip padding + } + + return ctx->mipMapSizes[mipmaplevel]; +} + +uint32_t TinyKtx_ImageSize(TinyKtx_ContextHandle handle, uint32_t mipmaplevel) { + return imageSize(handle, mipmaplevel, false); +} + +void const *TinyKtx_ImageRawData(TinyKtx_ContextHandle handle, uint32_t mipmaplevel) { + TinyKtx_Context *ctx = (TinyKtx_Context *) handle; + if (ctx == NULL) + return NULL; + + if (ctx->headerValid == false) { + ctx->callbacks.error(ctx->user, "Header data hasn't been read yet or its invalid"); + return NULL; + } + + if (mipmaplevel >= ctx->header.numberOfMipmapLevels) { + ctx->callbacks.error(ctx->user, "Invalid mipmap level"); + return NULL; + } + + if (mipmaplevel >= TINYKTX_MAX_MIPMAPLEVELS) { + ctx->callbacks.error(ctx->user, "Invalid mipmap level"); + return NULL; + } + + if (ctx->mipmaps[mipmaplevel] != NULL) + return ctx->mipmaps[mipmaplevel]; + + uint32_t size = imageSize(handle, mipmaplevel, true); + if (size == 0) + return NULL; + + ctx->mipmaps[mipmaplevel] = (uint8_t const*) ctx->callbacks.alloc(ctx->user, size); + if (ctx->mipmaps[mipmaplevel]) { + ctx->callbacks.read(ctx->user, (void *) ctx->mipmaps[mipmaplevel], size); + } + + return ctx->mipmaps[mipmaplevel]; +} + +#if TINYKTX_WANT_TINYKTX_FORMAT != 0 + +#define FT(fmt, type, intfmt, size) *glformat = TINYKTX_GL_FORMAT_##fmt; \ + *gltype = TINYKTX_GL_TYPE_##type; \ + *glinternalformat = TINYKTX_GL_INTFORMAT_##intfmt; \ + *typesize = size; \ + return true; +#define FTC(fmt, intfmt) *glformat = TINYKTX_GL_FORMAT_##fmt; \ + *gltype = TINYKTX_GL_TYPE_COMPRESSED; \ + *glinternalformat = TINYKTX_GL_COMPRESSED_##intfmt; \ + *typesize = 1; \ + return true; + +bool TinyKtx_CrackFormatToGL(TinyKtx_Format format, + uint32_t *glformat, + uint32_t *gltype, + uint32_t *glinternalformat, + uint32_t *typesize) { + switch (format) { + case TKTX_R4G4_UNORM_PACK8: break; + case TKTX_R4G4B4A4_UNORM_PACK16: FT(RGBA, UNSIGNED_SHORT_4_4_4_4, RGBA4, 1) + case TKTX_B4G4R4A4_UNORM_PACK16: FT(BGRA, UNSIGNED_SHORT_4_4_4_4_REV, RGBA4, 1) + case TKTX_R5G6B5_UNORM_PACK16: FT(RGB, UNSIGNED_SHORT_5_6_5, RGB565, 1) + case TKTX_B5G6R5_UNORM_PACK16: FT(BGR, UNSIGNED_SHORT_5_6_5_REV, RGB565, 1) + case TKTX_R5G5B5A1_UNORM_PACK16: FT(RGBA, UNSIGNED_SHORT_5_5_5_1, RGB5_A1, 1) + case TKTX_A1R5G5B5_UNORM_PACK16: FT(RGBA, UNSIGNED_SHORT_1_5_5_5_REV, RGB5_A1, 1) + case TKTX_B5G5R5A1_UNORM_PACK16: FT(BGRA, UNSIGNED_SHORT_5_5_5_1, RGB5_A1, 1) + + case TKTX_A2R10G10B10_UNORM_PACK32: FT(BGRA, UNSIGNED_INT_2_10_10_10_REV, RGB10_A2, 1) + case TKTX_A2R10G10B10_UINT_PACK32: FT(BGRA_INTEGER, UNSIGNED_INT_2_10_10_10_REV, RGB10_A2, 1) + case TKTX_A2B10G10R10_UNORM_PACK32: FT(RGBA, UNSIGNED_INT_2_10_10_10_REV, RGB10_A2, 1) + case TKTX_A2B10G10R10_UINT_PACK32: FT(RGBA_INTEGER, UNSIGNED_INT_2_10_10_10_REV, RGB10_A2, 1) + + case TKTX_R8_UNORM: FT(RED, UNSIGNED_BYTE, R8, 1) + case TKTX_R8_SNORM: FT(RED, BYTE, R8_SNORM, 1) + case TKTX_R8_UINT: FT(RED_INTEGER, UNSIGNED_BYTE, R8UI, 1) + case TKTX_R8_SINT: FT(RED_INTEGER, BYTE, R8I, 1) + case TKTX_R8_SRGB: FT(SLUMINANCE, UNSIGNED_BYTE, SRGB8, 1) + + case TKTX_R8G8_UNORM: FT(RG, UNSIGNED_BYTE, RG8, 1) + case TKTX_R8G8_SNORM: FT(RG, BYTE, RG8_SNORM, 1) + case TKTX_R8G8_UINT: FT(RG_INTEGER, UNSIGNED_BYTE, RG8UI, 1) + case TKTX_R8G8_SINT: FT(RG_INTEGER, BYTE, RG8I, 1) + case TKTX_R8G8_SRGB: FT(SLUMINANCE_ALPHA, UNSIGNED_BYTE, SRGB8, 1) + + case TKTX_R8G8B8_UNORM: FT(RGB, UNSIGNED_BYTE, RGB8, 1) + case TKTX_R8G8B8_SNORM: FT(RGB, BYTE, RGB8_SNORM, 1) + case TKTX_R8G8B8_UINT: FT(RGB_INTEGER, UNSIGNED_BYTE, RGB8UI, 1) + case TKTX_R8G8B8_SINT: FT(RGB_INTEGER, BYTE, RGB8I, 1) + case TKTX_R8G8B8_SRGB: FT(SRGB, UNSIGNED_BYTE, SRGB8, 1) + + case TKTX_B8G8R8_UNORM: FT(BGR, UNSIGNED_BYTE, RGB8, 1) + case TKTX_B8G8R8_SNORM: FT(BGR, BYTE, RGB8_SNORM, 1) + case TKTX_B8G8R8_UINT: FT(BGR_INTEGER, UNSIGNED_BYTE, RGB8UI, 1) + case TKTX_B8G8R8_SINT: FT(BGR_INTEGER, BYTE, RGB8I, 1) + case TKTX_B8G8R8_SRGB: FT(BGR, UNSIGNED_BYTE, SRGB8, 1) + + case TKTX_R8G8B8A8_UNORM:FT(RGBA, UNSIGNED_BYTE, RGBA8, 1) + case TKTX_R8G8B8A8_SNORM:FT(RGBA, BYTE, RGBA8_SNORM, 1) + case TKTX_R8G8B8A8_UINT: FT(RGBA_INTEGER, UNSIGNED_BYTE, RGBA8UI, 1) + case TKTX_R8G8B8A8_SINT: FT(RGBA_INTEGER, BYTE, RGBA8I, 1) + case TKTX_R8G8B8A8_SRGB: FT(SRGB_ALPHA, UNSIGNED_BYTE, SRGB8, 1) + + case TKTX_B8G8R8A8_UNORM: FT(BGRA, UNSIGNED_BYTE, RGBA8, 1) + case TKTX_B8G8R8A8_SNORM: FT(BGRA, BYTE, RGBA8_SNORM, 1) + case TKTX_B8G8R8A8_UINT: FT(BGRA_INTEGER, UNSIGNED_BYTE, RGBA8UI, 1) + case TKTX_B8G8R8A8_SINT: FT(BGRA_INTEGER, BYTE, RGBA8I, 1) + case TKTX_B8G8R8A8_SRGB: FT(BGRA, UNSIGNED_BYTE, SRGB8, 1) + + case TKTX_E5B9G9R9_UFLOAT_PACK32: FT(BGR, UNSIGNED_INT_5_9_9_9_REV, RGB9_E5, 1); + case TKTX_A8B8G8R8_UNORM_PACK32: FT(ABGR, UNSIGNED_BYTE, RGBA8, 1) + case TKTX_A8B8G8R8_SNORM_PACK32: FT(ABGR, BYTE, RGBA8, 1) + case TKTX_A8B8G8R8_UINT_PACK32: FT(ABGR, UNSIGNED_BYTE, RGBA8UI, 1) + case TKTX_A8B8G8R8_SINT_PACK32: FT(ABGR, BYTE, RGBA8I, 1) + case TKTX_A8B8G8R8_SRGB_PACK32: FT(ABGR, UNSIGNED_BYTE, SRGB8, 1) + case TKTX_B10G11R11_UFLOAT_PACK32: FT(BGR, UNSIGNED_INT_10F_11F_11F_REV, R11F_G11F_B10F, 1) + + case TKTX_R16_UNORM: FT(RED, UNSIGNED_SHORT, R16, 2) + case TKTX_R16_SNORM: FT(RED, SHORT, R16_SNORM, 2) + case TKTX_R16_UINT: FT(RED_INTEGER, UNSIGNED_SHORT, R16UI, 2) + case TKTX_R16_SINT: FT(RED_INTEGER, SHORT, R16I, 2) + case TKTX_R16_SFLOAT:FT(RED, HALF_FLOAT, R16F, 2) + + case TKTX_R16G16_UNORM: FT(RG, UNSIGNED_SHORT, RG16, 2) + case TKTX_R16G16_SNORM: FT(RG, SHORT, RG16_SNORM, 2) + case TKTX_R16G16_UINT: FT(RG_INTEGER, UNSIGNED_SHORT, RG16UI, 2) + case TKTX_R16G16_SINT: FT(RG_INTEGER, SHORT, RG16I, 2) + case TKTX_R16G16_SFLOAT:FT(RG, HALF_FLOAT, RG16F, 2) + + case TKTX_R16G16B16_UNORM: FT(RGB, UNSIGNED_SHORT, RGB16, 2) + case TKTX_R16G16B16_SNORM: FT(RGB, SHORT, RGB16_SNORM, 2) + case TKTX_R16G16B16_UINT: FT(RGB_INTEGER, UNSIGNED_SHORT, RGB16UI, 2) + case TKTX_R16G16B16_SINT: FT(RGB_INTEGER, SHORT, RGB16I, 2) + case TKTX_R16G16B16_SFLOAT: FT(RGB, HALF_FLOAT, RGB16F, 2) + + case TKTX_R16G16B16A16_UNORM: FT(RGBA, UNSIGNED_SHORT, RGBA16, 2) + case TKTX_R16G16B16A16_SNORM: FT(RGBA, SHORT, RGBA16_SNORM, 2) + case TKTX_R16G16B16A16_UINT: FT(RGBA_INTEGER, UNSIGNED_SHORT, RGBA16UI, 2) + case TKTX_R16G16B16A16_SINT: FT(RGBA_INTEGER, SHORT, RGBA16I, 2) + case TKTX_R16G16B16A16_SFLOAT:FT(RGBA, HALF_FLOAT, RGBA16F, 2) + + case TKTX_R32_UINT: FT(RED_INTEGER, UNSIGNED_INT, R32UI, 4) + case TKTX_R32_SINT: FT(RED_INTEGER, INT, R32I, 4) + case TKTX_R32_SFLOAT: FT(RED, FLOAT, R32F, 4) + + case TKTX_R32G32_UINT: FT(RG_INTEGER, UNSIGNED_INT, RG32UI, 4) + case TKTX_R32G32_SINT: FT(RG_INTEGER, INT, RG32I, 4) + case TKTX_R32G32_SFLOAT: FT(RG, FLOAT, RG32F, 4) + + case TKTX_R32G32B32_UINT: FT(RGB_INTEGER, UNSIGNED_INT, RGB32UI, 4) + case TKTX_R32G32B32_SINT: FT(RGB_INTEGER, INT, RGB32I, 4) + case TKTX_R32G32B32_SFLOAT: FT(RGB_INTEGER, FLOAT, RGB32F, 4) + + case TKTX_R32G32B32A32_UINT: FT(RGBA_INTEGER, UNSIGNED_INT, RGBA32UI, 4) + case TKTX_R32G32B32A32_SINT: FT(RGBA_INTEGER, INT, RGBA32I, 4) + case TKTX_R32G32B32A32_SFLOAT:FT(RGBA, FLOAT, RGBA32F, 4) + + case TKTX_BC1_RGB_UNORM_BLOCK: FTC(RGB, RGB_S3TC_DXT1) + case TKTX_BC1_RGB_SRGB_BLOCK: FTC(RGB, SRGB_S3TC_DXT1) + case TKTX_BC1_RGBA_UNORM_BLOCK: FTC(RGBA, RGBA_S3TC_DXT1) + case TKTX_BC1_RGBA_SRGB_BLOCK: FTC(RGBA, SRGB_ALPHA_S3TC_DXT1) + case TKTX_BC2_UNORM_BLOCK: FTC(RGBA, RGBA_S3TC_DXT3) + case TKTX_BC2_SRGB_BLOCK: FTC(RGBA, SRGB_ALPHA_S3TC_DXT3) + case TKTX_BC3_UNORM_BLOCK: FTC(RGBA, RGBA_S3TC_DXT5) + case TKTX_BC3_SRGB_BLOCK: FTC(RGBA, SRGB_ALPHA_S3TC_DXT5) + case TKTX_BC4_UNORM_BLOCK: FTC(RED, RED_RGTC1) + case TKTX_BC4_SNORM_BLOCK: FTC(RED, SIGNED_RED_RGTC1) + case TKTX_BC5_UNORM_BLOCK: FTC(RG, RED_GREEN_RGTC2) + case TKTX_BC5_SNORM_BLOCK: FTC(RG, SIGNED_RED_GREEN_RGTC2) + case TKTX_BC6H_UFLOAT_BLOCK: FTC(RGB, RGB_BPTC_UNSIGNED_FLOAT) + case TKTX_BC6H_SFLOAT_BLOCK: FTC(RGB, RGB_BPTC_SIGNED_FLOAT) + case TKTX_BC7_UNORM_BLOCK: FTC(RGBA, RGBA_BPTC_UNORM) + case TKTX_BC7_SRGB_BLOCK: FTC(RGBA, SRGB_ALPHA_BPTC_UNORM) + + case TKTX_ETC2_R8G8B8_UNORM_BLOCK: FTC(RGB, RGB8_ETC2) + case TKTX_ETC2_R8G8B8A1_UNORM_BLOCK: FTC(RGBA, RGB8_PUNCHTHROUGH_ALPHA1_ETC2) + case TKTX_ETC2_R8G8B8A8_UNORM_BLOCK: FTC(RGBA, RGBA8_ETC2_EAC) + case TKTX_ETC2_R8G8B8_SRGB_BLOCK: FTC(SRGB, SRGB8_ETC2) + case TKTX_ETC2_R8G8B8A1_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) + case TKTX_ETC2_R8G8B8A8_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ETC2_EAC) + case TKTX_EAC_R11_UNORM_BLOCK: FTC(RED, R11_EAC) + case TKTX_EAC_R11G11_UNORM_BLOCK: FTC(RG, RG11_EAC) + case TKTX_EAC_R11_SNORM_BLOCK: FTC(RED, SIGNED_R11_EAC) + case TKTX_EAC_R11G11_SNORM_BLOCK: FTC(RG, SIGNED_RG11_EAC) + + case TKTX_PVR_2BPP_BLOCK: FTC(RGB, RGB_PVRTC_2BPPV1) + case TKTX_PVR_2BPPA_BLOCK: FTC(RGBA, RGBA_PVRTC_2BPPV1); + case TKTX_PVR_4BPP_BLOCK: FTC(RGB, RGB_PVRTC_4BPPV1) + case TKTX_PVR_4BPPA_BLOCK: FTC(RGB, RGB_PVRTC_4BPPV1) + case TKTX_PVR_2BPP_SRGB_BLOCK: FTC(SRGB, SRGB_PVRTC_2BPPV1) + case TKTX_PVR_2BPPA_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB_ALPHA_PVRTC_2BPPV1); + case TKTX_PVR_4BPP_SRGB_BLOCK: FTC(SRGB, SRGB_PVRTC_2BPPV1) + case TKTX_PVR_4BPPA_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB_ALPHA_PVRTC_2BPPV1); + + case TKTX_ASTC_4x4_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_4x4) + case TKTX_ASTC_4x4_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_4x4) + case TKTX_ASTC_5x4_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_5x4) + case TKTX_ASTC_5x4_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_5x4) + case TKTX_ASTC_5x5_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_5x5) + case TKTX_ASTC_5x5_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_5x5) + case TKTX_ASTC_6x5_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_6x5) + case TKTX_ASTC_6x5_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_6x5) + case TKTX_ASTC_6x6_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_6x6) + case TKTX_ASTC_6x6_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_6x6) + case TKTX_ASTC_8x5_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_8x5) + case TKTX_ASTC_8x5_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_8x5) + case TKTX_ASTC_8x6_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_8x6) + case TKTX_ASTC_8x6_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_8x6) + case TKTX_ASTC_8x8_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_8x8) + case TKTX_ASTC_8x8_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_8x8) + case TKTX_ASTC_10x5_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_10x5) + case TKTX_ASTC_10x5_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_10x5) + case TKTX_ASTC_10x6_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_10x6) + case TKTX_ASTC_10x6_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_10x6) + case TKTX_ASTC_10x8_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_10x8); + case TKTX_ASTC_10x8_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_10x8) + case TKTX_ASTC_10x10_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_10x10) + case TKTX_ASTC_10x10_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_10x10) + case TKTX_ASTC_12x10_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_12x10) + case TKTX_ASTC_12x10_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_12x10) + case TKTX_ASTC_12x12_UNORM_BLOCK: FTC(RGBA, RGBA_ASTC_12x12) + case TKTX_ASTC_12x12_SRGB_BLOCK: FTC(SRGB_ALPHA, SRGB8_ALPHA8_ASTC_12x12) + + default:break; + } + return false; +} +#undef FT +#undef FTC + +TinyKtx_Format TinyKtx_CrackFormatFromGL(uint32_t const glformat, + uint32_t const gltype, + uint32_t const glinternalformat, + uint32_t const typesize) { + switch (glinternalformat) { + case TINYKTX_GL_COMPRESSED_RGB_S3TC_DXT1: return TKTX_BC1_RGB_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_S3TC_DXT1: return TKTX_BC1_RGBA_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_S3TC_DXT3: return TKTX_BC2_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_S3TC_DXT5: return TKTX_BC3_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_3DC_X_AMD: return TKTX_BC4_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_3DC_XY_AMD: return TKTX_BC5_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB_PVRTC_2BPPV1: return TKTX_PVR_2BPP_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB_PVRTC_4BPPV1: return TKTX_PVR_2BPPA_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1: return TKTX_PVR_2BPPA_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1: return TKTX_PVR_4BPPA_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_RGB_PVRTC_4BPPV1: return TKTX_PVR_4BPP_BLOCK; + case TINYKTX_GL_COMPRESSED_RGB_PVRTC_2BPPV1: return TKTX_PVR_2BPP_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: return TKTX_PVR_4BPPA_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: return TKTX_PVR_2BPPA_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB_S3TC_DXT1: return TKTX_BC1_RGB_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1: return TKTX_BC1_RGBA_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3: return TKTX_BC2_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5: return TKTX_BC3_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_LUMINANCE_LATC1: return TKTX_BC4_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SIGNED_LUMINANCE_LATC1: return TKTX_BC4_SNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_LUMINANCE_ALPHA_LATC2: return TKTX_BC5_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2: return TKTX_BC5_SNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RED_RGTC1: return TKTX_BC4_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SIGNED_RED_RGTC1: return TKTX_BC4_SNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RED_GREEN_RGTC2: return TKTX_BC5_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2: return TKTX_BC5_SNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_ETC1_RGB8_OES: return TKTX_ETC2_R8G8B8_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_BPTC_UNORM: return TKTX_BC7_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: return TKTX_BC7_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: return TKTX_BC6H_SFLOAT_BLOCK; + case TINYKTX_GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: return TKTX_BC6H_UFLOAT_BLOCK; + case TINYKTX_GL_COMPRESSED_R11_EAC: return TKTX_EAC_R11_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SIGNED_R11_EAC: return TKTX_EAC_R11_SNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RG11_EAC: return TKTX_EAC_R11G11_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SIGNED_RG11_EAC: return TKTX_EAC_R11G11_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGB8_ETC2: return TKTX_ETC2_R8G8B8_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ETC2: return TKTX_ETC2_R8G8B8_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: return TKTX_ETC2_R8G8B8A1_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return TKTX_ETC2_R8G8B8A1_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA8_ETC2_EAC: return TKTX_ETC2_R8G8B8A8_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return TKTX_ETC2_R8G8B8A8_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_4x4: return TKTX_ASTC_4x4_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_5x4: return TKTX_ASTC_5x4_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_5x5: return TKTX_ASTC_5x5_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_6x5: return TKTX_ASTC_6x5_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_6x6: return TKTX_ASTC_6x6_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_8x5: return TKTX_ASTC_8x5_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_8x6: return TKTX_ASTC_8x6_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_8x8: return TKTX_ASTC_8x8_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_10x5: return TKTX_ASTC_10x5_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_10x6: return TKTX_ASTC_10x6_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_10x8: return TKTX_ASTC_10x8_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_10x10: return TKTX_ASTC_10x10_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_12x10: return TKTX_ASTC_12x10_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_RGBA_ASTC_12x12: return TKTX_ASTC_12x12_UNORM_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: return TKTX_ASTC_4x4_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: return TKTX_ASTC_5x4_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: return TKTX_ASTC_5x5_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: return TKTX_ASTC_6x5_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: return TKTX_ASTC_6x6_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: return TKTX_ASTC_8x5_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: return TKTX_ASTC_8x6_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: return TKTX_ASTC_8x8_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: return TKTX_ASTC_10x5_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: return TKTX_ASTC_10x6_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: return TKTX_ASTC_10x8_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: return TKTX_ASTC_10x10_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: return TKTX_ASTC_12x10_SRGB_BLOCK; + case TINYKTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: return TKTX_ASTC_12x12_SRGB_BLOCK; + + // non compressed + case TINYKTX_GL_INTFORMAT_R8: return TKTX_R8_UNORM; + case TINYKTX_GL_INTFORMAT_RG8: return TKTX_R8G8_UNORM; + case TINYKTX_GL_INTFORMAT_RGB8: return TKTX_R8G8B8_UNORM; + case TINYKTX_GL_INTFORMAT_RGBA8: + if(glformat == TINYKTX_GL_FORMAT_RGBA) { + if(gltype == TINYKTX_GL_TYPE_UNSIGNED_BYTE) { + return TKTX_R8G8B8A8_UNORM; + } else if(gltype == TINYKTX_GL_TYPE_BYTE) { + return TKTX_R8G8B8A8_SNORM; + } + } else if(glformat == TINYKTX_GL_FORMAT_BGRA) { + if(gltype == TINYKTX_GL_TYPE_UNSIGNED_BYTE) { + return TKTX_B8G8R8A8_UNORM; + } else if(gltype == TINYKTX_GL_TYPE_BYTE) { + return TKTX_B8G8R8A8_SNORM; + } + } else if(glformat == TINYKTX_GL_FORMAT_ABGR) { + if(gltype == TINYKTX_GL_TYPE_UNSIGNED_BYTE) { + return TKTX_A8B8G8R8_UNORM_PACK32; + } else if(gltype == TINYKTX_GL_TYPE_BYTE) { + return TKTX_A8B8G8R8_SNORM_PACK32; + } + } + break; + case TINYKTX_GL_INTFORMAT_R8_SNORM: return TKTX_R8_SNORM; + case TINYKTX_GL_INTFORMAT_RG8_SNORM: return TKTX_R8G8_SNORM; + case TINYKTX_GL_INTFORMAT_RGB8_SNORM: return TKTX_R8G8B8_SNORM; + case TINYKTX_GL_INTFORMAT_RGBA8_SNORM: return TKTX_R8G8B8A8_SNORM; + + case TINYKTX_GL_INTFORMAT_R16: return TKTX_R16_UNORM; + case TINYKTX_GL_INTFORMAT_RG16: return TKTX_R16G16_UNORM; + case TINYKTX_GL_INTFORMAT_RGB16: return TKTX_R16G16B16_UNORM; + case TINYKTX_GL_INTFORMAT_RGBA16: return TKTX_R16G16B16A16_UNORM; + + case TINYKTX_GL_INTFORMAT_R16_SNORM:return TKTX_R16_SNORM; + case TINYKTX_GL_INTFORMAT_RG16_SNORM: return TKTX_R16G16_SNORM; + case TINYKTX_GL_INTFORMAT_RGB16_SNORM: return TKTX_R16G16B16_SNORM; + case TINYKTX_GL_INTFORMAT_RGBA16_SNORM: return TKTX_R16G16B16A16_SNORM; + + case TINYKTX_GL_INTFORMAT_R8I: return TKTX_R8_SINT; + case TINYKTX_GL_INTFORMAT_RG8I: return TKTX_R8G8_SINT; + case TINYKTX_GL_INTFORMAT_RGB8I:return TKTX_R8G8B8_SINT; + case TINYKTX_GL_INTFORMAT_RGBA8I: + if(glformat == TINYKTX_GL_FORMAT_RGBA || glformat == TINYKTX_GL_FORMAT_RGBA_INTEGER) { + return TKTX_R8G8B8A8_SINT; + } else if(glformat == TINYKTX_GL_FORMAT_BGRA || glformat == TINYKTX_GL_FORMAT_BGRA_INTEGER) { + return TKTX_B8G8R8A8_SINT; + } else if(glformat == TINYKTX_GL_FORMAT_ABGR) { + return TKTX_A8B8G8R8_SINT_PACK32; + } + break; + + case TINYKTX_GL_INTFORMAT_R16I: return TKTX_R16_SINT; + case TINYKTX_GL_INTFORMAT_RG16I: return TKTX_R16G16_SINT; + case TINYKTX_GL_INTFORMAT_RGB16I:return TKTX_R16G16B16_SINT; + case TINYKTX_GL_INTFORMAT_RGBA16I:return TKTX_R16G16B16A16_SINT; + + case TINYKTX_GL_INTFORMAT_R32I: return TKTX_R32_SINT; + case TINYKTX_GL_INTFORMAT_RG32I: return TKTX_R32G32_SINT; + case TINYKTX_GL_INTFORMAT_RGB32I: return TKTX_R32G32B32_SINT; + case TINYKTX_GL_INTFORMAT_RGBA32I:return TKTX_R32G32B32A32_SINT; + + case TINYKTX_GL_INTFORMAT_R8UI: return TKTX_R8_UINT; + case TINYKTX_GL_INTFORMAT_RG8UI: return TKTX_R8G8_UINT; + case TINYKTX_GL_INTFORMAT_RGB8UI: return TKTX_R8G8B8_UINT; + case TINYKTX_GL_INTFORMAT_RGBA8UI: + if(glformat == TINYKTX_GL_FORMAT_RGBA || glformat == TINYKTX_GL_FORMAT_RGBA_INTEGER) { + return TKTX_R8G8B8A8_UINT; + } else if(glformat == TINYKTX_GL_FORMAT_BGRA || glformat == TINYKTX_GL_FORMAT_BGRA_INTEGER) { + return TKTX_B8G8R8A8_UINT; + } else if(glformat == TINYKTX_GL_FORMAT_ABGR) { + return TKTX_A8B8G8R8_UINT_PACK32; + } + break; + + case TINYKTX_GL_INTFORMAT_R16UI: return TKTX_R16_UINT; + case TINYKTX_GL_INTFORMAT_RG16UI: return TKTX_R16G16_UINT; + case TINYKTX_GL_INTFORMAT_RGB16UI:return TKTX_R16G16B16_UINT; + case TINYKTX_GL_INTFORMAT_RGBA16UI: return TKTX_R16G16B16A16_UINT; + + case TINYKTX_GL_INTFORMAT_R32UI: return TKTX_R32_UINT; + case TINYKTX_GL_INTFORMAT_RG32UI: return TKTX_R32G32_UINT; + case TINYKTX_GL_INTFORMAT_RGB32UI: return TKTX_R32G32B32_UINT; + case TINYKTX_GL_INTFORMAT_RGBA32UI: return TKTX_R32G32B32A32_UINT; + + case TINYKTX_GL_INTFORMAT_R16F: return TKTX_R16_SFLOAT; + case TINYKTX_GL_INTFORMAT_RG16F: return TKTX_R16G16_SFLOAT; + case TINYKTX_GL_INTFORMAT_RGB16F: return TKTX_R16G16B16_SFLOAT; + case TINYKTX_GL_INTFORMAT_RGBA16F: return TKTX_R16G16B16A16_SFLOAT; + + case TINYKTX_GL_INTFORMAT_R32F: return TKTX_R32_SFLOAT; + case TINYKTX_GL_INTFORMAT_RG32F: return TKTX_R32G32_SFLOAT; + case TINYKTX_GL_INTFORMAT_RGB32F: return TKTX_R32G32B32_SFLOAT; + case TINYKTX_GL_INTFORMAT_RGBA32F: return TKTX_R32G32B32A32_SFLOAT; + + case TINYKTX_GL_INTFORMAT_R11F_G11F_B10F: return TKTX_B10G11R11_UFLOAT_PACK32; //?? + case TINYKTX_GL_INTFORMAT_UNSIGNED_INT_10F_11F_11F_REV: return TKTX_B10G11R11_UFLOAT_PACK32; //? + case TINYKTX_GL_INTFORMAT_RGB9_E5: return TKTX_E5B9G9R9_UFLOAT_PACK32; + case TINYKTX_GL_INTFORMAT_SLUMINANCE8_ALPHA8: return TKTX_R8G8_SRGB; + case TINYKTX_GL_INTFORMAT_SLUMINANCE8: return TKTX_R8_SRGB; + case TINYKTX_GL_INTFORMAT_RGB565: return TKTX_R5G6B5_UNORM_PACK16; + + case TINYKTX_GL_INTFORMAT_ALPHA8: return TKTX_R8_UNORM; + case TINYKTX_GL_INTFORMAT_ALPHA16: return TKTX_R16_UNORM; + case TINYKTX_GL_INTFORMAT_LUMINANCE8: return TKTX_R8_UNORM; + case TINYKTX_GL_INTFORMAT_LUMINANCE16: return TKTX_R16_UNORM; + case TINYKTX_GL_INTFORMAT_LUMINANCE8_ALPHA8: return TKTX_R8G8_UNORM; + case TINYKTX_GL_INTFORMAT_LUMINANCE16_ALPHA16: return TKTX_R16G16_UNORM; + case TINYKTX_GL_INTFORMAT_INTENSITY8: return TKTX_R8_UNORM; + case TINYKTX_GL_INTFORMAT_INTENSITY16: return TKTX_R16_UNORM; + + case TINYKTX_GL_INTFORMAT_ALPHA8_SNORM: return TKTX_R8_SNORM; + case TINYKTX_GL_INTFORMAT_LUMINANCE8_SNORM: return TKTX_R8_SNORM; + case TINYKTX_GL_INTFORMAT_LUMINANCE8_ALPHA8_SNORM: return TKTX_R8G8_SNORM; + case TINYKTX_GL_INTFORMAT_INTENSITY8_SNORM: return TKTX_R8_SNORM; + case TINYKTX_GL_INTFORMAT_ALPHA16_SNORM: return TKTX_R16_SNORM; + case TINYKTX_GL_INTFORMAT_LUMINANCE16_SNORM: return TKTX_R16_SNORM; + case TINYKTX_GL_INTFORMAT_LUMINANCE16_ALPHA16_SNORM: return TKTX_R16G16_SNORM; + case TINYKTX_GL_INTFORMAT_INTENSITY16_SNORM: return TKTX_R16_SNORM; + + case TINYKTX_GL_INTFORMAT_RGB5_A1: + if (gltype == TINYKTX_GL_TYPE_UNSIGNED_SHORT_1_5_5_5_REV) { + return TKTX_A1R5G5B5_UNORM_PACK16; + } else if (gltype == TINYKTX_GL_TYPE_UNSIGNED_SHORT_5_5_5_1) { + return TKTX_R5G5B5A1_UNORM_PACK16; + } + break; + + case TINYKTX_GL_INTFORMAT_SRGB8: + case TINYKTX_GL_INTFORMAT_SRGB8_ALPHA8: + if (glformat == TINYKTX_GL_FORMAT_SLUMINANCE) { + return TKTX_R8_SRGB; + } else if (glformat == TINYKTX_GL_FORMAT_SLUMINANCE_ALPHA) { + return TKTX_R8G8_SRGB; + } else if (glformat == TINYKTX_GL_FORMAT_SRGB) { + return TKTX_R8G8B8_SRGB; + } else if (glformat == TINYKTX_GL_FORMAT_SRGB_ALPHA) { + return TKTX_R8G8B8A8_SRGB; + } else if (glformat == TINYKTX_GL_FORMAT_BGR) { + return TKTX_B8G8R8_SRGB; + } else if (glformat == TINYKTX_GL_FORMAT_BGRA) { + return TKTX_B8G8R8A8_SRGB; + } else if (glformat == TINYKTX_GL_FORMAT_ABGR) { + return TKTX_A8B8G8R8_SRGB_PACK32; + } + break; + case TINYKTX_GL_INTFORMAT_RGBA4: + if(glformat == TINYKTX_GL_FORMAT_RGBA) { + return TKTX_R4G4B4A4_UNORM_PACK16; + } else if(glformat == TINYKTX_GL_FORMAT_BGRA) { + return TKTX_B4G4R4A4_UNORM_PACK16; + } + break; + + case TINYKTX_GL_INTFORMAT_RGB10_A2: + if(glformat == TINYKTX_GL_FORMAT_BGRA) { + return TKTX_A2R10G10B10_UNORM_PACK32; + } else if(glformat == TINYKTX_GL_FORMAT_RGBA) { + return TKTX_A2B10G10R10_UNORM_PACK32; + } else if(glformat == TINYKTX_GL_FORMAT_BGRA_INTEGER) { + return TKTX_A2R10G10B10_UINT_PACK32; + } else if(glformat == TINYKTX_GL_FORMAT_RGBA_INTEGER) { + return TKTX_A2B10G10R10_UINT_PACK32; + } + break; + + // can't handle yet + case TINYKTX_GL_INTFORMAT_ALPHA4: + case TINYKTX_GL_INTFORMAT_ALPHA12: + case TINYKTX_GL_INTFORMAT_LUMINANCE4: + case TINYKTX_GL_INTFORMAT_LUMINANCE12: + case TINYKTX_GL_INTFORMAT_LUMINANCE4_ALPHA4: + case TINYKTX_GL_INTFORMAT_LUMINANCE6_ALPHA2: + case TINYKTX_GL_INTFORMAT_LUMINANCE12_ALPHA4: + case TINYKTX_GL_INTFORMAT_LUMINANCE12_ALPHA12: + case TINYKTX_GL_INTFORMAT_INTENSITY4: + case TINYKTX_GL_INTFORMAT_INTENSITY12: + case TINYKTX_GL_INTFORMAT_RGB2: + case TINYKTX_GL_INTFORMAT_RGB4: + case TINYKTX_GL_INTFORMAT_RGB5: + case TINYKTX_GL_INTFORMAT_RGB10: + case TINYKTX_GL_INTFORMAT_RGB12: + case TINYKTX_GL_INTFORMAT_RGBA2: + case TINYKTX_GL_INTFORMAT_RGBA12: + case TINYKTX_GL_INTFORMAT_FLOAT_32_UNSIGNED_INT_24_8_REV: + case TINYKTX_GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2: + case TINYKTX_GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2: + case TINYKTX_GL_COMPRESSED_ATC_RGB: + case TINYKTX_GL_COMPRESSED_ATC_RGBA_EXPLICIT_ALPHA: + case TINYKTX_GL_COMPRESSED_ATC_RGBA_INTERPOLATED_ALPHA: + default: break; + } + return TKTX_UNDEFINED; +} + +TinyKtx_Format TinyKtx_GetFormat(TinyKtx_ContextHandle handle) { + uint32_t glformat; + uint32_t gltype; + uint32_t glinternalformat; + uint32_t typesize; + uint32_t glbaseinternalformat; + + if(TinyKtx_GetFormatGL(handle, &glformat, &gltype, &glinternalformat, &typesize, &glbaseinternalformat) == false) + return TKTX_UNDEFINED; + + return TinyKtx_CrackFormatFromGL(glformat, gltype, glinternalformat, typesize); +} + +bool TinyKtx_WriteImage(TinyKtx_WriteCallbacks const *callbacks, + void *user, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t slices, + uint32_t mipmaplevels, + TinyKtx_Format format, + bool cubemap, + uint32_t const *mipmapsizes, + void const **mipmaps) { + uint32_t glformat; + uint32_t glinternalFormat; + uint32_t gltype; + uint32_t gltypeSize; + if (TinyKtx_CrackFormatToGL(format, &glformat, &gltype, &glinternalFormat, &gltypeSize) == false) + return false; + + return TinyKtx_WriteImageGL(callbacks, + user, + width, + height, + depth, + slices, + mipmaplevels, + glformat, + glinternalFormat, + glinternalFormat, //?? + gltype, + gltypeSize, + cubemap, + mipmapsizes, + mipmaps + ); + +} +#endif // end #if TINYKTX_WANT_TINYKTX_FORMAT != 0 + +#endif + +#ifdef __cplusplus +}; +#endif + +#endif // end header +/* +BSD 2-Clause License + +Copyright (c) 2019, DeanoC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/