@mainpage
libktx is a small library of functions for creating and reading KTX (Khronos
TeXture) files, version 1 and 2 and instantiating OpenGL® and OpenGL® ES
textures and Vulkan images from them. KTX version 2 files can contain images
supercompressed with _zstd_ or _zlib_. They can also contain images in the Basis
Universal formats. libktx can deflate and inflate zstd and zlib compressed
images, can encode and transcode the Basis Universal formats and can
encode ASTC formats.
For information about the KTX format see the
formal specification.
@authors
Mark Callow,
formerly at HI Corporation\n
Mátyás Császár and Daniel Rákos, RasterGrid\n
Wasim Abbas, Arm\n
Andreas Atteneder, Independent\n
Georg Kolling, Imagination Technology\n
Jacob Ström, Ericsson AB
@snippet{doc} version.h API version
$Date$
# Usage Overview {#overview}
The following `ktxTexture` examples work for both KTX and KTX2 textures. The
texture type is determined from the file contents.
## Reading a KTX file for non-GL and non-Vulkan Use {#readktx}
~~~~~~~~~~~~~~~~{.c}
#include
ktxTexture* texture;
KTX_error_code result;
ktx_size_t offset;
ktx_uint8_t* image;
ktx_uint32_t level, layer, faceSlice;
result = ktxTexture_CreateFromNamedFile("mytex3d.ktx",
KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT,
&texture);
// Retrieve information about the texture from fields in the ktxTexture
// such as:
ktx_uint32_t numLevels = texture->numLevels;
ktx_uint32_t baseWidth = texture->baseWidth;
ktx_bool_t isArray = texture->isArray;
// Retrieve a pointer to the image for a specific mip level, array layer
// & face or depth slice.
level = 1; layer = 0; faceSlice = 3;
result = ktxTexture_GetImageOffset(texture, level, layer, faceSlice, &offset);
image = ktxTexture_GetData(texture) + offset;
// ...
// Do something with the texture image.
// ...
ktxTexture_Destroy(texture);
~~~~~~~~~~~~~~~~
## Creating a GL texture object from a KTX file. {#createGL}
~~~~~~~~~~~~~~~~{.c}
#include
ktxTexture* kTexture;
KTX_error_code result;
ktx_size_t offset;
ktx_uint8_t* image;
ktx_uint32_t level, layer, faceSlice;
GLuint texture = 0;
GLenum target, glerror;
result = ktxTexture_CreateFromNamedFile("mytex3d.ktx",
KTX_TEXTURE_CREATE_NO_FLAGS,
&kTexture);
// Before the first call to ktxTexture_GLUpload make libktx load its
// function pointers for the GL functions it uses. The parameter is a
// pointer to the GLGetProcAddress function provided by whatever OpenGL
// framework the application is using. The example shown is for SDL.
//
// Note 1: This is unrelated to any GL function pointers the app may be
// using.
// Note 2: When this is not called, libktx has fallback mechanisms to
// find the pointers which work on the vast majority of
// platforms. The only known failures have occurred on Fedora.
result = ktxLoadOpenGL((PFNGLGETPROCADDRESS)SDL_GL_GetProcAddress);
glGenTextures(1, &texture); // Optional. GLUpload can generate a texture.
result = ktxTexture_GLUpload(kTexture, &texture, &target, &glerror);
ktxTexture_Destroy(kTexture);
// ...
// GL rendering using the texture
// ...
~~~~~~~~~~~~~~~~
## Creating a Vulkan image object from a KTX file. {#createVulkan}
~~~~~~~~~~~~~~~~{.c}
#include
#include
ktxTexture* kTexture;
KTX_error_code result;
ktx_size_t offset;
ktx_uint8_t* image;
ktx_uint32_t level, layer, faceSlice;
ktxVulkanDeviceInfo vdi;
ktxVulkanTexture texture;
// Set up Vulkan physical device (gpu), logical device (device), queue
// and command pool. Save the handles to these in a struct called vkctx.
// ktx VulkanDeviceInfo is used to pass these with the expectation that
// apps are likely to upload a large number of textures.
ktxVulkanDeviceInfo_Construct(&vdi, vkctx.gpu, vkctx.device,
vkctx.queue, vkctx.commandPool, nullptr);
ktxresult = ktxTexture_CreateFromNamedFile("mytex3d.ktx",
KTX_TEXTURE_CREATE_NO_FLAGS,
&kTexture);
ktxresult = ktxTexture_VkUploadEx(kTexture, &vdi, &texture,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
ktxTexture_Destroy(kTexture);
ktxVulkanDeviceInfo_Destruct(&vdi);
// ...
// Vulkan rendering using the texture
// ...
// When done using the image in Vulkan...
ktxVulkanTexture_Destruct(&texture, vkctx.device, nullptr);
~~~~~~~~~~~~~~~~
## Extracting Metadata {#subsection}
Once a ktxTexture object has been created, metadata can be easily found
and extracted. The following can be added to any of the above.
~~~~~~~~~~~~~~~~{.c}
char* pValue;
uint32_t valueLen;
if (KTX_SUCCESS == ktxHashList_FindValue(&kTexture->kvDataHead,
KTX_ORIENTATION_KEY,
&valueLen, (void**)&pValue))
{
char s, t;
if (sscanf(pValue, KTX_ORIENTATION2_FMT, &s, &t) == 2) {
...
}
}
~~~~~~~~~~~~~~~~
## Writing a KTX or KTX2 file {#writektx}
~~~~~~~~~~~~~~~~{.c}
#include
#include
ktxTexture2* texture; // For KTX2
//ktxTexture1* texture; // For KTX
ktxTextureCreateInfo createInfo;
KTX_error_code result;
ktx_uint32_t level, layer, faceSlice;
FILE* src;
ktx_size_t srcSize;
createInfo.glInternalformat = GL_RGB8; // Ignored if creating a ktxTexture2.
createInfo.vkFormat = VK_FORMAT_R8G8B8_UNORM; // Ignored if creating a ktxTexture1.
createInfo.baseWidth = 2048;
createInfo.baseHeight = 1024;
createInfo.baseDepth = 16;
createInfo.numDimensions = 3.
// Note: it is not necessary to provide a full mipmap pyramid.
createInfo.numLevels = log2(createInfo.baseWidth) + 1
createInfo.numLayers = 1;
createInfo.numFaces = 1;
createInfo.isArray = KTX_FALSE;
createInfo.generateMipmaps = KTX_FALSE;
// Call ktxTexture1_Create to create a KTX texture.
result = ktxTexture2_Create(&createInfo,
KTX_TEXTURE_CREATE_ALLOC_STORAGE,
&texture);
src = // Open a stdio FILE* on the baseLevel image, slice 0.
srcSize = // Query size of the file.
level = 0;
layer = 0;
faceSlice = 0;
result = ktxTexture_SetImageFromMemory(ktxTexture(texture),
level, layer, faceSlice,
src, srcSize);
// Repeat for the other 15 slices of the base level and all other levels
// up to createInfo.numLevels.
ktxTexture_WriteToNamedFile(ktxTexture(texture), "mytex3d.ktx");
ktxTexture_Destroy(ktxTexture(texture));
~~~~~~~~~~~~~~~~
## Modifying a KTX file {#modifyktx}
~~~~~~~~~~~~~~~~{.c}
#include
ktxTexture* texture;
KTX_error_code result;
ktx_size_t offset;
ktx_uint8_t* image;
ktx_uint32_t level, layer, faceSlice;
result = ktxTexture_CreateFromNamedFile("mytex3d.ktx",
KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT,
&texture);
// The file is closed after all the data has been read.
// It is the responsibilty of the application to make sure its
// modifications are valid.
texture->generateMipmaps = KTX_TRUE;
ktxTexture_WriteToNamedFile(texture, "mytex3d.ktx");
ktxTexture_Destroy(texture);
~~~~~~~~~~~~~~~~
## Writing a Basis-compressed Universal Texture
Basis compression supports two universal texture formats: _BasisLZ/ETC1S_ and _UASTC_. The latter gives higher quality at a larger file size. Textures can be compressed to either format using `ktxTexture2_CompressBasisEx` as shown in this example.
~~~~~~~~~~~~~~~~{.c}
#include
#include
ktxTexture2* texture;
ktxTextureCreateInfo createInfo;
KTX_error_code result;
ktx_uint32_t level, layer, faceSlice;
FILE* src;
ktx_size_t srcSize;
ktxBasisParams params = {0};
params.structSize = sizeof(params);
createInfo.glInternalformat = 0; //Ignored as we'll create a KTX2 texture.
createInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
createInfo.baseWidth = 2048;
createInfo.baseHeight = 1024;
createInfo.baseDepth = 16;
createInfo.numDimensions = 3.
// Note: it is not necessary to provide a full mipmap pyramid.
createInfo.numLevels = log2(createInfo.baseWidth) + 1
createInfo.numLayers = 1;
createInfo.numFaces = 1;
createInfo.isArray = KTX_FALSE;
createInfo.generateMipmaps = KTX_FALSE;
result = ktxTexture2_Create(&createInfo,
KTX_TEXTURE_CREATE_ALLOC_STORAGE,
&texture);
src = // Open the file for the baseLevel image, slice 0 and
// read it into memory.
srcSize = // Query size of the file.
level = 0;
layer = 0;
faceSlice = 0;
result = ktxTexture_SetImageFromMemory(ktxTexture(texture),
level, layer, faceSlice,
src, srcSize);
// Repeat for the other 15 slices of the base level and all other levels
// up to createInfo.numLevels.
// For BasisLZ/ETC1S
params.compressionLevel = KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL;
// For UASTC
params.uastc = KTX_TRUE;
// Set other BasisLZ/ETC1S or UASTC params to change default quality settings.
result = ktxtexture2_CompressBasisEx(texture, ¶ms);
ktxTexture_WriteToNamedFile(ktxTexture(texture), "mytex3d.ktx2");
ktxTexture_Destroy(ktxTexture(texture));
~~~~~~~~~~~~~~~~
There is a shortcut that can be used when compressing to BasisLZ/ETC1S. Remove
the declaration and initialization of `params` in the previous example and
replace `ktxtexture2_CompressBasisEx` with
~~~~~~~~~~~~~~~~{.c}
// Quality range is 1 - 255. 0 gets the default quality, currently 128.
// The qualityLevel field in ktxBasisParams is set from this.
int quality = 0;
result = ktxTexture2_CompressBasis(texture, quality);
~~~~~~~~~~~~~~~~
## Transcoding a BasisLZ/ETC1S or UASTC-compressed Texture
~~~~~~~~~~~~~~~~{.c}
#include
ktxTexture2* texture;
KTX_error_code result;
result = ktxTexture_CreateFromNamedFile("mytex3d_basis.ktx2",
KTX_TEXTURE_CREATE_NO_FLAGS,
(ktxTexture**)&kTexture);
// or
//result = ktxTexture2_CreateFromNamedFile("mytex3d_basis.ktx2",
// KTX_TEXTURE_CREATE_NO_FLAGS,
// &kTexture);
if (ktxTexture2_NeedsTranscoding(texture)) {
ktx_texture_transcode_fmt_e tf;
// Using VkGetPhysicalDeviceFeatures or GL_COMPRESSED_TEXTURE_FORMATS or
// extension queries, determine what compressed texture formats are
// supported and pick a format. For example
vk::PhysicalDeviceFeatures deviceFeatures;
vkctx.gpu.getFeatures(&deviceFeatures);
khr_df_model_e colorModel = ktxTexture2_GetColorModel_e(texture);
if (colorModel == KHR_DF_MODEL_UASTC
&& deviceFeatures.textureCompressionASTC_LDR) {
tf = KTX_TTF_ASTC_4x4_RGBA;
} else if (colorModel == KHR_DF_MODEL_ETC1S
&& deviceFeatures.textureCompressionETC2) {
tf = KTX_TTF_ETC;
} else if (deviceFeatures.textureCompressionASTC_LDR) {
tf = KTX_TTF_ASTC_4x4_RGBA;
} else if (deviceFeatures.textureCompressionETC2)
tf = KTX_TTF_ETC2_RGBA;
else if (deviceFeatures.textureCompressionBC)
tf = KTX_TTF_BC3_RGBA;
else {
message << "Vulkan implementation does not support any available transcode target.";
throw std::runtime_error(message.str());
}
result = ktxTexture2_TranscodeBasis(texture, tf, 0);
// Then use VkUpload or GLUpload to create a texture object on the GPU.
}
~~~~~~~~~~~~~~~~
## Writing an ASTC-Compressed Texture
~~~~~~~~~~~~~~~~{.c}
#include
#include
ktxTexture2* texture;
ktxTextureCreateInfo createInfo;
KTX_error_code result;
ktx_uint32_t level, layer, faceSlice;
FILE* src;
ktx_size_t srcSize;
ktxAstcParams params = {0};
params.structSize = sizeof(params);
createInfo.glInternalformat = 0; //Ignored as we'll create a KTX2 texture.
createInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
createInfo.baseWidth = 2048;
createInfo.baseHeight = 1024;
createInfo.baseDepth = 16;
createInfo.numDimensions = 3.
// Note: it is not necessary to provide a full mipmap pyramid.
createInfo.numLevels = log2(createInfo.baseWidth) + 1
createInfo.numLayers = 1;
createInfo.numFaces = 1;
createInfo.isArray = KTX_FALSE;
createInfo.generateMipmaps = KTX_FALSE;
result = ktxTexture2_Create(&createInfo,
KTX_TEXTURE_CREATE_ALLOC_STORAGE,
&texture);
src = // Open the file for the baseLevel image, slice 0 and
// read it into memory.
srcSize = // Query size of the file.
level = 0;
layer = 0;
faceSlice = 0;
result = ktxTexture_SetImageFromMemory(ktxTexture(texture),
level, layer, faceSlice,
src, srcSize);
// Repeat for the other 15 slices of the base level and all other levels
// up to createInfo.numLevels.
params.threadCount = 1;
params.blockDimension = KTX_PACK_ASTC_BLOCK_DIMENSION_6x6;
params.mode = KTX_PACK_ASTC_ENCODER_MODE_LDR;
params.qualityLevel = KTX_PACK_ASTC_QUALITY_LEVEL_MEDIUM;
result = ktxtexture2_CompressAstcEx(texture, ¶ms);
ktxTexture_WriteToNamedFile(ktxTexture(texture), "mytex3d.ktx2");
ktxTexture_Destroy(ktxTexture(texture));
~~~~~~~~~~~~~~~~
There is a shortcut that can be used when the only `params` field you want to
modify is the `qualityLevel`. Remove the declaration and initialization of
`params` in the previous example and replace `ktxtexture2_CompressAstcEx` with
~~~~~~~~~~~~~~~~{.c}
// Quality range is 0 - 100. 0 is fastest/lowest. 100 is slowest/highest.
int quality = KTX_PACK_ASTC_QUALITY_LEVEL_MEDIUM;
result = ktxTexture2_CompressAstc(texture, quality);
~~~~~~~~~~~~~~~~