This commit is contained in:
2026-06-14 19:09:18 +01:00
parent 14bd1a9271
commit 13fa90a0e9
3958 changed files with 999286 additions and 4 deletions
+14
View File
@@ -0,0 +1,14 @@
# Copyright 2024 The Khronos Group Inc.
# SPDX-License-Identifier: Apache-2.0
---
# Use defaults from the Google style with the following exceptions:
Language: Cpp
BasedOnStyle: Google
IndentWidth: 4
IndentCaseLabels: false
AccessModifierOffset: -2
ColumnLimit: 100
SortIncludes: false
AlwaysBreakAfterDefinitionReturnType: TopLevel
IndentPPDirectives: BeforeHash
...
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+85
View File
@@ -0,0 +1,85 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2019-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file basisu_sgd.h
* @~English
*
* @brief Declare global data for Basis LZ supercompression with ETC1S.
*
* These functions are private and should not be used outside the library.
*/
#ifndef _BASIS_SGD_H_
#define _BASIS_SGD_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// This must be the same value as cSliceDescFlagsFrameIsIFrame so we can just
// invert the bit when passing back & forth. As FrameIsIFrame is within
// a C namespace it can't easily be accessed from a c header.
enum bu_image_flags__bits_e { ETC1S_P_FRAME = 0x02 };
typedef uint32_t buFlags;
typedef struct ktxBasisLzGlobalHeader {
uint16_t endpointCount;
uint16_t selectorCount;
uint32_t endpointsByteLength;
uint32_t selectorsByteLength;
uint32_t tablesByteLength;
uint32_t extendedByteLength;
} ktxBasisLzGlobalHeader;
// This header is followed by imageCount "slice" descriptions.
// 1, or 2 slices per image (i.e. layer, face & slice).
// These offsets are relative to start of a mip level as given by the
// main levelIndex.
typedef struct ktxBasisLzEtc1sImageDesc {
buFlags imageFlags;
uint32_t rgbSliceByteOffset;
uint32_t rgbSliceByteLength;
uint32_t alphaSliceByteOffset;
uint32_t alphaSliceByteLength;
} ktxBasisLzEtc1sImageDesc;
#define BGD_ETC1S_IMAGE_DESCS(bgd) \
reinterpret_cast<ktxBasisLzEtc1sImageDesc*>(bgd + sizeof(ktxBasisLzGlobalHeader))
// The are followed in the global data by these ...
// uint8_t[endpointsByteLength] endpointsData;
// uint8_t[selectorsByteLength] selectorsData;
// uint8_t[tablesByteLength] tablesData;
#define BGD_ENDPOINTS_ADDR(bgd, imageCount) \
(bgd + sizeof(ktxBasisLzGlobalHeader) + sizeof(ktxBasisLzEtc1sImageDesc) * imageCount)
#define BGD_SELECTORS_ADDR(bgd, bgdh, imageCount) (BGD_ENDPOINTS_ADDR(bgd, imageCount) + bgdh.endpointsByteLength)
#define BGD_TABLES_ADDR(bgd, bgdh, imageCount) (BGD_SELECTORS_ADDR(bgd, bgdh, imageCount) + bgdh.selectorsByteLength)
#define BGD_EXTENDED_ADDR(bgd, bgdh, imageCount) (BGD_TABLES_ADDR(bgd, bgdh, imageCount) + bgdh.tablesByteLength)
// Just because this is a convenient place to put it for basis_{en,trans}code.
enum alpha_content_e {
eNone,
eAlpha,
eGreen
};
#ifdef __cplusplus
}
#endif
#endif /* _BASIS_SGD_H_ */
+744
View File
@@ -0,0 +1,744 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2019-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Functions for transcoding Basis Universal BasisLZ/ETC1S and UASTC textures.
*
* Two worlds collide here too. More uglyness!
*
* @author Mark Callow, github.com/MarkCallow
*/
#include <inttypes.h>
#include <stdio.h>
#include <KHR/khr_df.h>
#include "dfdutils/dfd.h"
#include "ktx.h"
#include "ktxint.h"
#include "texture2.h"
#include "vkformat_enum.h"
#include "vk_format.h"
#include "basis_sgd.h"
#include "basisu/transcoder/basisu_file_headers.h"
#include "basisu/transcoder/basisu_transcoder.h"
#include "basisu/transcoder/basisu_transcoder_internal.h"
#undef DECLARE_PRIVATE
#undef DECLARE_PROTECTED
#define DECLARE_PRIVATE(n,t2) ktxTexture2_private& n = *(t2->_private)
#define DECLARE_PROTECTED(n,t2) ktxTexture_protected& n = *(t2->_protected)
using namespace basisu;
using namespace basist;
inline bool isPow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
inline bool isPow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
KTX_error_code
ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
alpha_content_e alphaContent,
ktxTexture2* prototype,
ktx_transcode_fmt_e outputFormat,
ktx_transcode_flags transcodeFlags);
KTX_error_code
ktxTexture2_transcodeUastc(ktxTexture2* This,
alpha_content_e alphaContent,
ktxTexture2* prototype,
ktx_transcode_fmt_e outputFormat,
ktx_transcode_flags transcodeFlags);
/**
* @memberof ktxTexture2
* @ingroup reader
* @~English
* @brief Transcode a KTX2 texture with BasisLZ/ETC1S or UASTC images.
*
* If the texture contains BasisLZ supercompressed images, Inflates them from
* back to ETC1S then transcodes them to the specified block-compressed
* format. If the texture contains UASTC images, inflates them, if they have been
* supercompressed with zstd, then transcodes then to the specified format, The
* transcoded images replace the original images and the texture's fields including
* the DFD are modified to reflect the new format.
*
* These types of textures must be transcoded to a desired target
* block-compressed format before they can be uploaded to a GPU via a
* graphics API.
*
* The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
* @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
* @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
* @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
* @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
* @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
* @c KTX_TTF_BC1_OR_3.
*
* @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
* @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
* does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
* @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
* channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
*
* Transcoding to ATC & FXT1 formats is not supported by libktx as there
* are no equivalent Vulkan formats.
*
* The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
* @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
*
* The following @p transcodeFlags are available.
*
* @sa ktxtexture2_CompressBasis().
*
* @param[in] This pointer to the ktxTexture2 object of interest.
* @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum
* specifying the target format.
* @param[in] transcodeFlags bitfield of flags modifying the transcode
* operation. @sa ktx_texture_decode_flags_e.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_FILE_DATA_ERROR
* Supercompression global data is corrupted.
* @exception KTX_INVALID_OPERATION
* The texture's format is not transcodable (not
* ETC1S/BasisLZ or UASTC).
* @exception KTX_INVALID_OPERATION
* Supercompression global data is missing, i.e.,
* the texture object is invalid.
* @exception KTX_INVALID_OPERATION
* Image data is missing, i.e., the texture object
* is invalid.
* @exception KTX_INVALID_OPERATION
* @p outputFormat is PVRTC1 but the texture does
* does not have power-of-two dimensions.
* @exception KTX_INVALID_VALUE @p outputFormat is invalid.
* @exception KTX_TRANSCODE_FAILED
* Something went wrong during transcoding.
* @exception KTX_UNSUPPORTED_FEATURE
* KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
* or the specified transcode target has not been
* included in the library being used.
* @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
*/
KTX_error_code
ktxTexture2_TranscodeBasis(ktxTexture2* This,
ktx_transcode_fmt_e outputFormat,
ktx_transcode_flags transcodeFlags)
{
uint32_t* BDB = This->pDfd + 1;
khr_df_model_e colorModel = (khr_df_model_e)KHR_DFDVAL(BDB, MODEL);
if (colorModel != KHR_DF_MODEL_UASTC
// Constructor has checked color model matches BASIS_LZ.
&& This->supercompressionScheme != KTX_SS_BASIS_LZ)
{
return KTX_INVALID_OPERATION; // Not in a transcodable format.
}
DECLARE_PRIVATE(priv, This);
if (This->supercompressionScheme == KTX_SS_BASIS_LZ) {
if (!priv._supercompressionGlobalData || priv._sgdByteLength == 0)
return KTX_INVALID_OPERATION;
}
if (transcodeFlags & KTX_TF_PVRTC_DECODE_TO_NEXT_POW2) {
debug_printf("ktxTexture_TranscodeBasis: KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 currently unsupported\n");
return KTX_UNSUPPORTED_FEATURE;
}
if (outputFormat == KTX_TTF_PVRTC1_4_RGB
|| outputFormat == KTX_TTF_PVRTC1_4_RGBA) {
if ((!isPow2(This->baseWidth)) || (!isPow2(This->baseHeight))) {
debug_printf("ktxTexture_TranscodeBasis: PVRTC1 only supports power of 2 dimensions\n");
return KTX_INVALID_OPERATION;
}
}
const bool srgb = (KHR_DFDVAL(BDB, TRANSFER) == KHR_DF_TRANSFER_SRGB);
alpha_content_e alphaContent = eNone;
if (colorModel == KHR_DF_MODEL_ETC1S) {
if (KHR_DFDSAMPLECOUNT(BDB) == 2) {
uint32_t channelId = KHR_DFDSVAL(BDB, 1, CHANNELID);
if (channelId == KHR_DF_CHANNEL_ETC1S_AAA) {
alphaContent = eAlpha;
} else if (channelId == KHR_DF_CHANNEL_ETC1S_GGG){
alphaContent = eGreen;
} else {
return KTX_FILE_DATA_ERROR;
}
}
} else {
uint32_t channelId = KHR_DFDSVAL(BDB, 0, CHANNELID);
if (channelId == KHR_DF_CHANNEL_UASTC_RGBA)
alphaContent = eAlpha;
else if (channelId == KHR_DF_CHANNEL_UASTC_RRRG)
alphaContent = eGreen;
}
VkFormat vkFormat;
// Do some format mapping.
switch (outputFormat) {
case KTX_TTF_BC1_OR_3:
outputFormat = alphaContent != eNone ? KTX_TTF_BC3_RGBA
: KTX_TTF_BC1_RGB;
break;
case KTX_TTF_ETC:
outputFormat = alphaContent != eNone ? KTX_TTF_ETC2_RGBA
: KTX_TTF_ETC1_RGB;
break;
case KTX_TTF_PVRTC1_4_RGBA:
// This transcoder does not write opaque alpha blocks.
outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC1_4_RGBA
: KTX_TTF_PVRTC1_4_RGB;
break;
case KTX_TTF_PVRTC2_4_RGBA:
// This transcoder does not write opaque alpha blocks.
outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC2_4_RGBA
: KTX_TTF_PVRTC2_4_RGB;
break;
default:
/*NOP*/;
}
switch (outputFormat) {
case KTX_TTF_ETC1_RGB:
vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK
: VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
break;
case KTX_TTF_ETC2_RGBA:
vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK
: VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
break;
case KTX_TTF_ETC2_EAC_R11:
vkFormat = VK_FORMAT_EAC_R11_UNORM_BLOCK;
break;
case KTX_TTF_ETC2_EAC_RG11:
vkFormat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
break;
case KTX_TTF_BC1_RGB:
// Transcoding doesn't support BC1 alpha.
vkFormat = srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK
: VK_FORMAT_BC1_RGB_UNORM_BLOCK;
break;
case KTX_TTF_BC3_RGBA:
vkFormat = srgb ? VK_FORMAT_BC3_SRGB_BLOCK
: VK_FORMAT_BC3_UNORM_BLOCK;
break;
case KTX_TTF_BC4_R:
vkFormat = VK_FORMAT_BC4_UNORM_BLOCK;
break;
case KTX_TTF_BC5_RG:
vkFormat = VK_FORMAT_BC5_UNORM_BLOCK;
break;
case KTX_TTF_PVRTC1_4_RGB:
case KTX_TTF_PVRTC1_4_RGBA:
vkFormat = srgb ? VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG
: VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
break;
case KTX_TTF_PVRTC2_4_RGB:
case KTX_TTF_PVRTC2_4_RGBA:
vkFormat = srgb ? VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG
: VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;
break;
case KTX_TTF_BC7_RGBA:
vkFormat = srgb ? VK_FORMAT_BC7_SRGB_BLOCK
: VK_FORMAT_BC7_UNORM_BLOCK;
break;
case KTX_TTF_ASTC_4x4_RGBA:
vkFormat = srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK
: VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
break;
case KTX_TTF_RGB565:
vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
break;
case KTX_TTF_BGR565:
vkFormat = VK_FORMAT_B5G6R5_UNORM_PACK16;
break;
case KTX_TTF_RGBA4444:
vkFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16;
break;
case KTX_TTF_RGBA32:
vkFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB
: VK_FORMAT_R8G8B8A8_UNORM;
break;
default:
return KTX_INVALID_VALUE;
}
basis_tex_format textureFormat;
if (colorModel == KHR_DF_MODEL_UASTC)
textureFormat = basis_tex_format::cUASTC4x4;
else
textureFormat = basis_tex_format::cETC1S;
if (!basis_is_format_supported((transcoder_texture_format)outputFormat,
textureFormat)) {
return KTX_UNSUPPORTED_FEATURE;
}
// Create a prototype texture to use for calculating sizes in the target
// format and, as useful side effects, provide us with a properly sized
// data allocation and the DFD for the target format.
ktxTextureCreateInfo createInfo;
createInfo.glInternalformat = 0;
createInfo.vkFormat = vkFormat;
createInfo.baseWidth = This->baseWidth;
createInfo.baseHeight = This->baseHeight;
createInfo.baseDepth = This->baseDepth;
createInfo.generateMipmaps = This->generateMipmaps;
createInfo.isArray = This->isArray;
createInfo.numDimensions = This->numDimensions;
createInfo.numFaces = This->numFaces;
createInfo.numLayers = This->numLayers;
createInfo.numLevels = This->numLevels;
createInfo.pDfd = nullptr;
KTX_error_code result;
ktxTexture2* prototype;
result = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE,
&prototype);
if (result != KTX_SUCCESS) {
assert(result == KTX_OUT_OF_MEMORY); // The only run time error
return result;
}
if (!This->pData) {
if (ktxTexture_isActiveStream((ktxTexture*)This)) {
// Load pending. Complete it.
result = ktxTexture2_LoadImageData(This, NULL, 0);
if (result != KTX_SUCCESS)
{
ktxTexture2_Destroy(prototype);
return result;
}
} else {
// No data to transcode.
ktxTexture2_Destroy(prototype);
return KTX_INVALID_OPERATION;
}
}
// Transcoder global initialization. Requires ~9 milliseconds when compiled
// and executed natively on a Core i7 2.2 GHz. If this is too slow, the
// tables it computes can easily be moved to be compiled in.
static bool transcoderInitialized;
if (!transcoderInitialized) {
basisu_transcoder_init();
transcoderInitialized = true;
}
if (textureFormat == basis_tex_format::cETC1S) {
result = ktxTexture2_transcodeLzEtc1s(This, alphaContent,
prototype, outputFormat,
transcodeFlags);
} else {
result = ktxTexture2_transcodeUastc(This, alphaContent,
prototype, outputFormat,
transcodeFlags);
}
if (result == KTX_SUCCESS) {
// Fix up the current texture
DECLARE_PROTECTED(thisPrtctd, This);
DECLARE_PRIVATE(protoPriv, prototype);
DECLARE_PROTECTED(protoPrtctd, prototype);
memcpy(&thisPrtctd._formatSize, &protoPrtctd._formatSize,
sizeof(ktxFormatSize));
This->vkFormat = vkFormat;
This->isCompressed = prototype->isCompressed;
This->supercompressionScheme = KTX_SS_NONE;
priv._requiredLevelAlignment = protoPriv._requiredLevelAlignment;
// Copy the levelIndex from the prototype to This.
memcpy(priv._levelIndex, protoPriv._levelIndex,
This->numLevels * sizeof(ktxLevelIndexEntry));
// Move the DFD and data from the prototype to This.
free(This->pDfd);
This->pDfd = prototype->pDfd;
prototype->pDfd = 0;
free(This->pData);
This->pData = prototype->pData;
This->dataSize = prototype->dataSize;
prototype->pData = 0;
prototype->dataSize = 0;
// Free SGD data
This->_private->_sgdByteLength = 0;
if (This->_private->_supercompressionGlobalData) {
free(This->_private->_supercompressionGlobalData);
This->_private->_supercompressionGlobalData = NULL;
}
}
ktxTexture2_Destroy(prototype);
return result;
}
/**
* @memberof ktxTexture2 @private
* @ingroup reader
* @~English
* @brief Transcode a KTX2 texture with BasisLZ supercompressed ETC1S images.
*
* Inflates the images from BasisLZ supercompression back to ETC1S
* then transcodes them to the specified block-compressed format. The
* transcoded images replace the original images and the texture's fields
* including the DFD are modified to reflect the new format.
*
* BasisLZ supercompressed textures must be transcoded to a desired target
* block-compressed format before they can be uploaded to a GPU via a graphics
* API.
*
* The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
* @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
* @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
* @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
* @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
* @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
* @c KTX_TTF_BC1_OR_3.
*
* @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
* @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
* does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
* @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
* channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
*
* ATC & FXT1 formats are not supported by KTX2 & libktx as there are no equivalent Vulkan formats.
*
* The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
* @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
*
* The following @p transcodeFlags are available.
*
* @sa ktxtexture2_CompressBasis().
*
* @param[in] This pointer to the ktxTexture2 object of interest.
* @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum
* specifying the target format.
* @param[in] transcodeFlags bitfield of flags modifying the transcode
* operation. @sa ktx_texture_decode_flags_e.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_FILE_DATA_ERROR
* Supercompression global data is corrupted.
* @exception KTX_INVALID_OPERATION
* The texture's format is not transcodable (not
* ETC1S/BasisLZ or UASTC).
* @exception KTX_INVALID_OPERATION
* Supercompression global data is missing, i.e.,
* the texture object is invalid.
* @exception KTX_INVALID_OPERATION
* Image data is missing, i.e., the texture object
* is invalid.
* @exception KTX_INVALID_OPERATION
* @p outputFormat is PVRTC1 but the texture does
* does not have power-of-two dimensions.
* @exception KTX_INVALID_VALUE @p outputFormat is invalid.
* @exception KTX_TRANSCODE_FAILED
* Something went wrong during transcoding. The
* texture object will be corrupted.
* @exception KTX_UNSUPPORTED_FEATURE
* KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
* or the specified transcode target has not been
* included in the library being used.
* @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
*/
KTX_error_code
ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
alpha_content_e alphaContent,
ktxTexture2* prototype,
ktx_transcode_fmt_e outputFormat,
ktx_transcode_flags transcodeFlags)
{
DECLARE_PRIVATE(priv, This);
DECLARE_PRIVATE(protoPriv, prototype);
KTX_error_code result = KTX_SUCCESS;
assert(This->supercompressionScheme == KTX_SS_BASIS_LZ);
uint8_t* bgd = priv._supercompressionGlobalData;
ktxBasisLzGlobalHeader& bgdh = *reinterpret_cast<ktxBasisLzGlobalHeader*>(bgd);
if (!(bgdh.endpointsByteLength && bgdh.selectorsByteLength && bgdh.tablesByteLength)) {
debug_printf("ktxTexture_TranscodeBasis: missing endpoints, selectors or tables");
return KTX_FILE_DATA_ERROR;
}
// Compute some helpful numbers.
//
// firstImages contains the indices of the first images for each level to
// ease finding the correct slice description when iterating from smallest
// level to largest or when randomly accessing them (t.b.c). The last array
// entry contains the total number of images, for calculating the offsets
// of the endpoints, etc.
uint32_t* firstImages = new uint32_t[This->numLevels+1];
// Temporary invariant value
uint32_t layersFaces = This->numLayers * This->numFaces;
firstImages[0] = 0;
for (uint32_t level = 1; level <= This->numLevels; level++) {
// NOTA BENE: numFaces * depth is only reasonable because they can't
// both be > 1. I.e there are no 3d cubemaps.
firstImages[level] = firstImages[level - 1]
+ layersFaces * MAX(This->baseDepth >> (level - 1), 1);
}
uint32_t& imageCount = firstImages[This->numLevels];
if (BGD_TABLES_ADDR(0, bgdh, imageCount) + bgdh.tablesByteLength > priv._sgdByteLength) {
// Compiler will not allow `goto cleanup;` because "jump bypasses variable initialization."
// The static initializations below this and before the loop are presumably the issue
// as the compiler is,presumably, inserting code to destruct those at the end of the
// function.
delete[] firstImages;
return KTX_FILE_DATA_ERROR;
}
// FIXME: Do more validation.
// Prepare low-level transcoder for transcoding slices.
basist::basisu_lowlevel_etc1s_transcoder bit;
// basisu_transcoder_state is used to find the previous frame when
// decoding a video P-Frame. It tracks the previous frame for each mip
// level. For cube map array textures we need to find the previous frame
// for each face so we a state per face. Although providing this is only
// needed for video, it is easier to always pass our own.
std::vector<basisu_transcoder_state> xcoderStates;
xcoderStates.resize(This->isVideo ? This->numFaces : 1);
bit.decode_palettes(bgdh.endpointCount, BGD_ENDPOINTS_ADDR(bgd, imageCount),
bgdh.endpointsByteLength,
bgdh.selectorCount, BGD_SELECTORS_ADDR(bgd, bgdh, imageCount),
bgdh.selectorsByteLength);
bit.decode_tables(BGD_TABLES_ADDR(bgd, bgdh, imageCount),
bgdh.tablesByteLength);
// Find matching VkFormat and calculate output sizes.
const bool isVideo = This->isVideo;
ktx_uint8_t* pXcodedData = prototype->pData;
// Inconveniently, the output buffer size parameter of transcode_image
// has to be in pixels for uncompressed output and in blocks for
// compressed output. The only reason for humouring the API is so
// its buffer size tests provide a real check. An alternative is to
// always provide the size in bytes which will always pass.
ktx_uint32_t outputBlockByteLength
= prototype->_protected->_formatSize.blockSizeInBits / 8;
ktx_size_t xcodedDataLength
= prototype->dataSize / outputBlockByteLength;
ktxLevelIndexEntry* protoLevelIndex;
uint64_t levelOffsetWrite;
const ktxBasisLzEtc1sImageDesc* imageDescs = BGD_ETC1S_IMAGE_DESCS(bgd);
// Finally we're ready to transcode the slices.
// FIXME: Iframe flag needs to be queryable by the application. In Basis
// the app can query file_info and image_info from the transcoder which
// returns a structure with lots of info about the image.
protoLevelIndex = protoPriv._levelIndex;
levelOffsetWrite = 0;
for (int32_t level = This->numLevels - 1; level >= 0; level--) {
uint64_t levelOffset = ktxTexture2_levelDataOffset(This, level);
uint64_t writeOffset = levelOffsetWrite;
uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
uint32_t levelWidth = MAX(1, This->baseWidth >> level);
uint32_t levelHeight = MAX(1, This->baseHeight >> level);
// ETC1S texel block dimensions
const uint32_t bw = 4, bh = 4;
uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
uint32_t depth = MAX(1, This->baseDepth >> level);
//uint32_t faceSlices = This->numFaces == 1 ? depth : This->numFaces;
uint32_t faceSlices = This->numFaces * depth;
uint32_t numImages = This->numLayers * faceSlices;
uint32_t image = firstImages[level];
uint32_t endImage = image + numImages;
ktx_size_t levelImageSizeOut, levelSizeOut;
uint32_t stateIndex = 0;
levelSizeOut = 0;
// FIXME: Figure out a way to get the size out of the transcoder.
levelImageSizeOut = ktxTexture2_GetImageSize(prototype, level);
for (; image < endImage; image++) {
const ktxBasisLzEtc1sImageDesc& imageDesc = imageDescs[image];
basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
// We have face0 [face1 ...] within each layer. Use `stateIndex`
// rather than a double loop of layers and faceSlices as this
// works for 3d texture and non-array cube maps as well as
// cube map arrays without special casing.
if (++stateIndex == xcoderStates.size())
stateIndex = 0;
if (alphaContent != eNone)
{
// The slice descriptions should have alpha information.
if (imageDesc.alphaSliceByteOffset == 0
|| imageDesc.alphaSliceByteLength == 0)
return KTX_FILE_DATA_ERROR;
}
bool status;
status = bit.transcode_image(
(transcoder_texture_format)outputFormat,
pXcodedData + writeOffset,
(uint32_t)(xcodedDataLength - writeOffsetBlocks),
This->pData,
(uint32_t)This->dataSize,
levelBlocksX,
levelBlocksY,
levelWidth,
levelHeight,
level,
(uint32_t)(levelOffset + imageDesc.rgbSliceByteOffset),
imageDesc.rgbSliceByteLength,
(uint32_t)(levelOffset + imageDesc.alphaSliceByteOffset),
imageDesc.alphaSliceByteLength,
transcodeFlags,
alphaContent != eNone,
isVideo,
// Our P-Frame flag is in the same bit as
// cSliceDescFlagsFrameIsIFrame. We have to
// invert it to make it an I-Frame flag.
//
// API currently doesn't have any way to pass
// the I-Frame flag.
//imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
0, // output_row_pitch_in_blocks_or_pixels
&xcoderState,
0 // output_rows_in_pixels
);
if (!status) {
result = KTX_TRANSCODE_FAILED;
goto cleanup;
}
writeOffset += levelImageSizeOut;
levelSizeOut += levelImageSizeOut;
} // end images loop
protoLevelIndex[level].byteOffset = levelOffsetWrite;
protoLevelIndex[level].byteLength = levelSizeOut;
protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
levelOffsetWrite += levelSizeOut;
assert(levelOffsetWrite == writeOffset);
// In case of transcoding to uncompressed.
levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
levelOffsetWrite);
} // level loop
result = KTX_SUCCESS;
cleanup:
delete[] firstImages;
return result;
}
KTX_error_code
ktxTexture2_transcodeUastc(ktxTexture2* This,
alpha_content_e alphaContent,
ktxTexture2* prototype,
ktx_transcode_fmt_e outputFormat,
ktx_transcode_flags transcodeFlags)
{
assert(This->supercompressionScheme != KTX_SS_BASIS_LZ);
ktx_uint8_t* pXcodedData = prototype->pData;
ktx_uint32_t outputBlockByteLength
= prototype->_protected->_formatSize.blockSizeInBits / 8;
ktx_size_t xcodedDataLength
= prototype->dataSize / outputBlockByteLength;
DECLARE_PRIVATE(protoPriv, prototype);
ktxLevelIndexEntry* protoLevelIndex = protoPriv._levelIndex;
ktx_size_t levelOffsetWrite = 0;
basisu_lowlevel_uastc_transcoder uit;
// See comment on same declaration in transcodeEtc1s.
std::vector<basisu_transcoder_state> xcoderStates;
xcoderStates.resize(This->isVideo ? This->numFaces : 1);
for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)
{
ktx_uint32_t depth;
uint64_t writeOffset = levelOffsetWrite;
uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
ktx_size_t levelImageSizeIn, levelImageOffsetIn;
ktx_size_t levelImageSizeOut, levelSizeOut;
ktx_uint32_t levelImageCount;
uint32_t levelWidth = MAX(1, This->baseWidth >> level);
uint32_t levelHeight = MAX(1, This->baseHeight >> level);
// UASTC texel block dimensions
const uint32_t bw = 4, bh = 4;
uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
uint32_t stateIndex = 0;
depth = MAX(1, This->baseDepth >> level);
levelImageCount = This->numLayers * This->numFaces * depth;
levelImageSizeIn = ktxTexture_calcImageSize(ktxTexture(This), level,
KTX_FORMAT_VERSION_TWO);
levelImageSizeOut = ktxTexture_calcImageSize(ktxTexture(prototype),
level,
KTX_FORMAT_VERSION_TWO);
levelImageOffsetIn = ktxTexture2_levelDataOffset(This, level);
levelSizeOut = 0;
bool status;
for (uint32_t image = 0; image < levelImageCount; image++) {
basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
// See comment before same lines in transcodeEtc1s.
if (++stateIndex == xcoderStates.size())
stateIndex = 0;
status = uit.transcode_image(
(transcoder_texture_format)outputFormat,
pXcodedData + writeOffset,
(uint32_t)(xcodedDataLength - writeOffsetBlocks),
This->pData,
(uint32_t)This->dataSize,
levelBlocksX,
levelBlocksY,
levelWidth,
levelHeight,
level,
(uint32_t)levelImageOffsetIn,
(uint32_t)levelImageSizeIn,
transcodeFlags,
alphaContent != eNone,
This->isVideo, // is_video
//imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
0, // output_row_pitch_in_blocks_or_pixels
&xcoderState, // pState
0, // output_rows_in_pixels,
-1, // channel0
-1 // channel1
);
if (!status)
return KTX_TRANSCODE_FAILED;
writeOffset += levelImageSizeOut;
levelSizeOut += levelImageSizeOut;
levelImageOffsetIn += levelImageSizeIn;
}
protoLevelIndex[level].byteOffset = levelOffsetWrite;
// writeOffset will be equal to total size of the images in the level.
protoLevelIndex[level].byteLength = levelSizeOut;
protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
levelOffsetWrite += levelSizeOut;
}
// In case of transcoding to uncompressed.
levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
levelOffsetWrite);
return KTX_SUCCESS;
}
+299
View File
@@ -0,0 +1,299 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/* $Id$ */
/*
* Copyright 2010-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Function to verify a KTX file header
*
* @author Mark Callow, HI Corporation
*/
/*
* Author: Georg Kolling, Imagination Technology with modifications
* by Mark Callow, HI Corporation.
*/
#include <assert.h>
#include <string.h>
#include "ktx.h"
#include "ktxint.h"
#include "vkformat_enum.h"
bool isProhibitedFormat(VkFormat format);
bool isValidFormat(VkFormat format);
/**
* @internal
* @~English
* @brief Check a KTX file header.
*
* As well as checking that the header identifies a KTX file, the function
* sanity checks the values and returns information about the texture in a
* struct KTX_supplementary_info.
*
* @param pHeader pointer to the KTX header to check
* @param pSuppInfo pointer to a KTX_supplementary_info structure in which to
* return information about the texture.
*
* @author Georg Kolling, Imagination Technology
* @author Mark Callow, HI Corporation
*/
KTX_error_code ktxCheckHeader1_(KTX_header* pHeader,
KTX_supplemental_info* pSuppInfo)
{
ktx_uint8_t identifier_reference[12] = KTX_IDENTIFIER_REF;
ktx_uint32_t max_dim;
assert(pHeader != NULL && pSuppInfo != NULL);
/* Compare identifier, is this a KTX file? */
if (memcmp(pHeader->identifier, identifier_reference, 12) != 0)
{
return KTX_UNKNOWN_FILE_FORMAT;
}
if (pHeader->endianness == KTX_ENDIAN_REF_REV)
{
/* Convert endianness of pHeader fields. */
_ktxSwapEndian32(&pHeader->glType, 12);
if (pHeader->glTypeSize != 1 &&
pHeader->glTypeSize != 2 &&
pHeader->glTypeSize != 4)
{
/* Only 8-, 16-, and 32-bit types supported so far. */
return KTX_FILE_DATA_ERROR;
}
}
else if (pHeader->endianness != KTX_ENDIAN_REF)
{
return KTX_FILE_DATA_ERROR;
}
/* Check glType and glFormat */
pSuppInfo->compressed = 0;
if (pHeader->glType == 0 || pHeader->glFormat == 0)
{
if (pHeader->glType + pHeader->glFormat != 0)
{
/* either both or none of glType, glFormat must be zero */
return KTX_FILE_DATA_ERROR;
}
pSuppInfo->compressed = 1;
}
if (pHeader->glFormat == pHeader->glInternalformat) {
// glInternalFormat is either unsized (which is no longer and should
// never have been supported by libktx) or glFormat is sized.
return KTX_FILE_DATA_ERROR;
}
/* Check texture dimensions. KTX files can store 8 types of textures:
1D, 2D, 3D, cube, and array variants of these. There is currently
no GL extension for 3D array textures. */
if ((pHeader->pixelWidth == 0) ||
(pHeader->pixelDepth > 0 && pHeader->pixelHeight == 0))
{
/* texture must have width */
/* texture must have height if it has depth */
return KTX_FILE_DATA_ERROR;
}
if (pHeader->pixelDepth > 0)
{
if (pHeader->numberOfArrayElements > 0)
{
/* No 3D array textures yet. */
return KTX_UNSUPPORTED_FEATURE;
}
pSuppInfo->textureDimension = 3;
}
else if (pHeader->pixelHeight > 0)
{
pSuppInfo->textureDimension = 2;
}
else
{
pSuppInfo->textureDimension = 1;
}
if (pHeader->numberOfFaces == 6)
{
if (pSuppInfo->textureDimension != 2)
{
/* cube map needs 2D faces */
return KTX_FILE_DATA_ERROR;
}
}
else if (pHeader->numberOfFaces != 1)
{
/* numberOfFaces must be either 1 or 6 */
return KTX_FILE_DATA_ERROR;
}
/* Check number of mipmap levels */
if (pHeader->numberOfMipLevels == 0)
{
pSuppInfo->generateMipmaps = 1;
pHeader->numberOfMipLevels = 1;
}
else
{
pSuppInfo->generateMipmaps = 0;
}
/* This test works for arrays too because height or depth will be 0. */
max_dim = MAX(MAX(pHeader->pixelWidth, pHeader->pixelHeight), pHeader->pixelDepth);
if (max_dim < ((ktx_uint32_t)1 << (pHeader->numberOfMipLevels - 1)))
{
/* Can't have more mip levels than 1 + log2(max(width, height, depth)) */
return KTX_FILE_DATA_ERROR;
}
return KTX_SUCCESS;
}
/**
* @internal
* @~English
* @brief Check a KTX2 file header.
*
* As well as checking that the header identifies a KTX 2 file, the function
* sanity checks the values and returns information about the texture in a
* struct KTX_supplementary_info.
*
* @param pHeader pointer to the KTX header to check
* @param pSuppInfo pointer to a KTX_supplementary_info structure in which to
* return information about the texture.
*
* @author Mark Callow, HI Corporation
*/
KTX_error_code ktxCheckHeader2_(KTX_header2* pHeader,
KTX_supplemental_info* pSuppInfo)
{
// supp info is compressed, generateMipmaps and num dimensions. Don't need
// compressed as formatSize gives us that. I think the other 2 aren't needed.
ktx_uint8_t identifier_reference[12] = KTX2_IDENTIFIER_REF;
assert(pHeader != NULL && pSuppInfo != NULL);
ktx_uint32_t max_dim;
/* Compare identifier, is this a KTX file? */
if (memcmp(pHeader->identifier, identifier_reference, 12) != 0)
{
return KTX_UNKNOWN_FILE_FORMAT;
}
/* Check format */
if (isProhibitedFormat(pHeader->vkFormat))
{
return KTX_FILE_DATA_ERROR;
}
if (!isValidFormat(pHeader->vkFormat))
{
return KTX_UNSUPPORTED_FEATURE;
}
if (pHeader->supercompressionScheme == KTX_SS_BASIS_LZ && pHeader->vkFormat != VK_FORMAT_UNDEFINED)
{
return KTX_FILE_DATA_ERROR;
}
/* Check texture dimensions. KTX files can store 8 types of textures:
1D, 2D, 3D, cube, and array variants of these. There is currently
no extension for 3D array textures in any 3D API. */
if ((pHeader->pixelWidth == 0) ||
(pHeader->pixelDepth > 0 && pHeader->pixelHeight == 0))
{
/* texture must have width */
/* texture must have height if it has depth */
return KTX_FILE_DATA_ERROR;
}
if (pHeader->pixelDepth > 0)
{
if (pHeader->layerCount > 0)
{
/* No 3D array textures yet. */
return KTX_UNSUPPORTED_FEATURE;
}
pSuppInfo->textureDimension = 3;
}
else if (pHeader->pixelHeight > 0)
{
pSuppInfo->textureDimension = 2;
}
else
{
pSuppInfo->textureDimension = 1;
}
if (pHeader->faceCount == 6)
{
if (pSuppInfo->textureDimension != 2)
{
/* cube map needs 2D faces */
return KTX_FILE_DATA_ERROR;
}
if (pHeader->pixelDepth != 0)
{
/* cube map cannot have depth */
return KTX_FILE_DATA_ERROR;
}
if (pHeader->pixelWidth != pHeader->pixelHeight)
{
/* cube map needs square faces */
return KTX_FILE_DATA_ERROR;
}
}
else if (pHeader->faceCount != 1)
{
/* numberOfFaces must be either 1 or 6 */
return KTX_FILE_DATA_ERROR;
}
// Check number of mipmap levels
if (pHeader->levelCount == 0)
{
pSuppInfo->generateMipmaps = 1;
pHeader->levelCount = 1;
}
else
{
pSuppInfo->generateMipmaps = 0;
}
// Check supercompression
switch (pHeader->supercompressionScheme) {
case KTX_SS_NONE:
case KTX_SS_BASIS_LZ:
case KTX_SS_ZSTD:
case KTX_SS_ZLIB:
break;
default:
// Unsupported supercompression
return KTX_UNSUPPORTED_FEATURE;
}
// This test works for arrays too because height or depth will be 0.
max_dim = MAX(MAX(pHeader->pixelWidth, pHeader->pixelHeight), pHeader->pixelDepth);
if (max_dim < ((ktx_uint32_t)1 << (pHeader->levelCount - 1)))
{
// Can't have more mip levels than 1 + log2(max(width, height, depth))
return KTX_FILE_DATA_ERROR;
}
return KTX_SUCCESS;
}
+277
View File
@@ -0,0 +1,277 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4: */
/* $Id: b84f0063f86e9f20473d5a900eecbf1bf57b399c $ */
/*
* Copyright 2010-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/* @internal
* @~English
* @file
*
* Unpack a texture compressed with ETC1
*
* @author Mark Callow, HI Corporation.
*/
#include <assert.h>
#include <stdlib.h>
#include "GL/glcorearb.h"
// Not defined in glcorearb.h.
#define GL_ETC1_RGB8_OES 0x8D64
#include "ktx.h"
#include "ktxint.h"
#if SUPPORT_SOFTWARE_ETC_UNPACK
typedef unsigned int uint;
typedef unsigned char uint8;
extern void decompressBlockETC2c(uint block_part1, uint block_part2, uint8* img,
int width, int height, int startx, int starty, int channels);
extern void decompressBlockETC21BitAlphaC(uint block_part1, uint block_part2, uint8* img, uint8* alphaimg,
int width, int height, int startx, int starty, int channels);
extern void decompressBlockAlphaC(uint8* data, uint8* img,
int width, int height, int startx, int starty, int channels);
extern void decompressBlockAlpha16bitC(uint8* data, uint8* img,
int width, int height, int startx, int starty, int channels);
extern void setupAlphaTable();
// This global variable affects the behaviour of decompressBlockAlpha16bitC.
extern int formatSigned;
static void
readBigEndian4byteWord(ktx_uint32_t* pBlock, const GLubyte *s)
{
*pBlock = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
}
/* Unpack an ETC1_RGB8_OES format compressed texture */
extern "C" KTX_error_code
_ktxUnpackETC(const GLubyte* srcETC, const GLenum srcFormat,
ktx_uint32_t activeWidth, ktx_uint32_t activeHeight,
GLubyte** dstImage,
GLenum* format, GLenum* internalFormat, GLenum* type,
GLint R16Formats, GLboolean supportsSRGB)
{
unsigned int width, height;
unsigned int block_part1, block_part2;
unsigned int x, y;
/*const*/ GLubyte* src = (GLubyte*)srcETC;
// AF_11BIT is used to compress R11 & RG11 though its not alpha data.
enum {AF_NONE, AF_1BIT, AF_8BIT, AF_11BIT} alphaFormat = AF_NONE;
int dstChannels, dstChannelBytes;
switch (srcFormat) {
case GL_COMPRESSED_SIGNED_R11_EAC:
if (R16Formats & _KTX_R16_FORMATS_SNORM) {
dstChannelBytes = sizeof(GLshort);
dstChannels = 1;
formatSigned = GL_TRUE;
*internalFormat = GL_R16_SNORM;
*format = GL_RED;
*type = GL_SHORT;
alphaFormat = AF_11BIT;
} else
return KTX_UNSUPPORTED_TEXTURE_TYPE;
break;
case GL_COMPRESSED_R11_EAC:
if (R16Formats & _KTX_R16_FORMATS_NORM) {
dstChannelBytes = sizeof(GLshort);
dstChannels = 1;
formatSigned = GL_FALSE;
*internalFormat = GL_R16;
*format = GL_RED;
*type = GL_UNSIGNED_SHORT;
alphaFormat = AF_11BIT;
} else
return KTX_UNSUPPORTED_TEXTURE_TYPE;
break;
case GL_COMPRESSED_SIGNED_RG11_EAC:
if (R16Formats & _KTX_R16_FORMATS_SNORM) {
dstChannelBytes = sizeof(GLshort);
dstChannels = 2;
formatSigned = GL_TRUE;
*internalFormat = GL_RG16_SNORM;
*format = GL_RG;
*type = GL_SHORT;
alphaFormat = AF_11BIT;
} else
return KTX_UNSUPPORTED_TEXTURE_TYPE;
break;
case GL_COMPRESSED_RG11_EAC:
if (R16Formats & _KTX_R16_FORMATS_NORM) {
dstChannelBytes = sizeof(GLshort);
dstChannels = 2;
formatSigned = GL_FALSE;
*internalFormat = GL_RG16;
*format = GL_RG;
*type = GL_UNSIGNED_SHORT;
alphaFormat = AF_11BIT;
} else
return KTX_UNSUPPORTED_TEXTURE_TYPE;
break;
case GL_ETC1_RGB8_OES:
case GL_COMPRESSED_RGB8_ETC2:
dstChannelBytes = sizeof(GLubyte);
dstChannels = 3;
*internalFormat = GL_RGB8;
*format = GL_RGB;
*type = GL_UNSIGNED_BYTE;
break;
case GL_COMPRESSED_RGBA8_ETC2_EAC:
dstChannelBytes = sizeof(GLubyte);
dstChannels = 4;
*internalFormat = GL_RGBA8;
*format = GL_RGBA;
*type = GL_UNSIGNED_BYTE;
alphaFormat = AF_8BIT;
break;
case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
dstChannelBytes = sizeof(GLubyte);
dstChannels = 4;
*internalFormat = GL_RGBA8;
*format = GL_RGBA;
*type = GL_UNSIGNED_BYTE;
alphaFormat = AF_1BIT;
break;
case GL_COMPRESSED_SRGB8_ETC2:
if (supportsSRGB) {
dstChannelBytes = sizeof(GLubyte);
dstChannels = 3;
*internalFormat = GL_SRGB8;
*format = GL_RGB;
*type = GL_UNSIGNED_BYTE;
} else
return KTX_UNSUPPORTED_TEXTURE_TYPE;
break;
case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
if (supportsSRGB) {
dstChannelBytes = sizeof(GLubyte);
dstChannels = 4;
*internalFormat = GL_SRGB8_ALPHA8;
*format = GL_RGBA;
*type = GL_UNSIGNED_BYTE;
alphaFormat = AF_8BIT;
} else
return KTX_UNSUPPORTED_TEXTURE_TYPE;
break;
case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
if (supportsSRGB) {
dstChannelBytes = sizeof(GLubyte);
dstChannels = 4;
*internalFormat = GL_SRGB8_ALPHA8;
*format = GL_RGBA;
*type = GL_UNSIGNED_BYTE;
alphaFormat = AF_1BIT;
} else
return KTX_UNSUPPORTED_TEXTURE_TYPE;
break;
default:
assert(0); // Upper levels should pass only one of the above srcFormats.
return KTX_UNSUPPORTED_TEXTURE_TYPE; // For Release configurations.
}
/* active_{width,height} show how many pixels contain active data,
* (the rest are just for making sure we have a 2*a x 4*b size).
*/
/* Compute the full width & height. */
width = ((activeWidth+3)/4)*4;
height = ((activeHeight+3)/4)*4;
/* printf("Width = %d, Height = %d\n", width, height); */
/* printf("active pixel area: top left %d x %d area.\n", activeWidth, activeHeight); */
*dstImage = (GLubyte*)malloc(dstChannels*dstChannelBytes*width*height);
if (!*dstImage) {
return KTX_OUT_OF_MEMORY;
}
if (alphaFormat != AF_NONE)
setupAlphaTable();
// NOTE: none of the decompress functions actually use the <height> parameter
if (alphaFormat == AF_11BIT) {
// One or two 11-bit alpha channels for R or RG.
for (y=0; y < height/4; y++) {
for (x=0; x < width/4; x++) {
decompressBlockAlpha16bitC(src, *dstImage, width, height, 4*x, 4*y, dstChannels);
src += 8;
if (srcFormat == GL_COMPRESSED_RG11_EAC || srcFormat == GL_COMPRESSED_SIGNED_RG11_EAC) {
decompressBlockAlpha16bitC(src, *dstImage + dstChannelBytes, width, height, 4*x, 4*y, dstChannels);
src += 8;
}
}
}
} else {
for (y=0; y < height/4; y++) {
for (x=0; x < width/4; x++) {
// Decode alpha channel for RGBA
if (alphaFormat == AF_8BIT) {
decompressBlockAlphaC(src, *dstImage + 3, width, height, 4*x, 4*y, dstChannels);
src += 8;
}
// Decode color dstChannels
readBigEndian4byteWord(&block_part1, src);
src += 4;
readBigEndian4byteWord(&block_part2, src);
src += 4;
if (alphaFormat == AF_1BIT)
decompressBlockETC21BitAlphaC(block_part1, block_part2, *dstImage, 0, width, height, 4*x, 4*y, dstChannels);
else
decompressBlockETC2c(block_part1, block_part2, *dstImage, width, height, 4*x, 4*y, dstChannels);
}
}
}
/* Ok, now write out the active pixels to the destination image.
* (But only if the active pixels differ from the total pixels)
*/
if( !(height == activeHeight && width == activeWidth) ) {
int dstPixelBytes = dstChannels * dstChannelBytes;
int dstRowBytes = dstPixelBytes * width;
int activeRowBytes = activeWidth * dstPixelBytes;
GLubyte *newimg = (GLubyte*)malloc(dstPixelBytes * activeWidth * activeHeight);
unsigned int xx, yy;
int zz;
if (!newimg) {
free(*dstImage);
return KTX_OUT_OF_MEMORY;
}
/* Convert from total area to active area: */
for (yy = 0; yy < activeHeight; yy++) {
for (xx = 0; xx < activeWidth; xx++) {
for (zz = 0; zz < dstPixelBytes; zz++) {
newimg[ yy*activeRowBytes + xx*dstPixelBytes + zz ] = (*dstImage)[ yy*dstRowBytes + xx*dstPixelBytes + zz];
}
}
}
free(*dstImage);
*dstImage = newimg;
}
return KTX_SUCCESS;
}
#endif /* SUPPORT_SOFTWARE_ETC_UNPACK */
+395
View File
@@ -0,0 +1,395 @@
/* -*- 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 FILE.
*
* @author Maksim Kolesin, Under Development
* @author Georg Kolling, Imagination Technology
* @author Mark Callow, HI Corporation
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
/* I need these on Linux. Why? */
#define __USE_LARGEFILE 1 // For declaration of ftello, etc.
#define __USE_POSIX 1 // For declaration of fileno.
#define _POSIX_SOURCE 1 // For both the above in Emscripten.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> // For stat.h on Windows
#define __USE_MISC 1 // For declaration of S_IF...
#include <sys/stat.h>
#include "ktx.h"
#include "ktxint.h"
#include "filestream.h"
// Gotta love Windows :-(
#if defined(_MSC_VER)
#if defined(_WIN64)
#define ftello _ftelli64
#define fseeko _fseeki64
#else
#define ftello ftell
#define fseeko fseek
#endif
#define fileno _fileno
#define fstat _fstat
#define stat _stat
#define S_IFIFO _S_IFIFO
#define S_IFSOCK 0xC000
typedef unsigned short mode_t;
#endif
#if defined(__MINGW32__)
#define S_IFSOCK 0xC000
#endif
#define KTX_FILE_STREAM_MAX (1 << (sizeof(ktx_off_t) - 1) - 1)
/**
* @~English
* @brief Read bytes from a ktxFileStream.
*
* @param [in] str pointer to the ktxStream from which to read.
* @param [out] dst pointer to a block of memory with a size
* of at least @p size bytes, converted to a void*.
* @param [in,out] count pointer to total count of bytes to be read.
* On completion set to number of bytes read.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p dst is @c NULL or @p src is @c NULL.
* @exception KTX_FILE_READ_ERROR an error occurred while reading the file.
* @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
*/
static
KTX_error_code ktxFileStream_read(ktxStream* str, void* dst, const ktx_size_t count)
{
ktx_size_t nread;
if (!str || !dst)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeFile);
if ((nread = fread(dst, 1, count, str->data.file)) != count) {
if (feof(str->data.file)) {
return KTX_FILE_UNEXPECTED_EOF;
} else {
return KTX_FILE_READ_ERROR;
}
}
str->readpos += count;
return KTX_SUCCESS;
}
/**
* @~English
* @brief Skip bytes in a ktxFileStream.
*
* @param [in] str pointer to a ktxStream object.
* @param [in] count number of bytes to be skipped.
*
* In order to support applications reading from stdin, read characters
* rather than using seek functions.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p str is @c NULL or @p count is less than zero.
* @exception KTX_INVALID_OPERATION skipping @p count bytes would go beyond EOF.
* @exception KTX_FILE_READ_ERROR an error occurred while reading the file.
* @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
* @p count is set to the number of bytes
* skipped.
*/
static
KTX_error_code ktxFileStream_skip(ktxStream* str, const ktx_size_t count)
{
if (!str)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeFile);
for (ktx_uint32_t i = 0; i < count; i++) {
int ret = getc(str->data.file);
if (ret == EOF) {
if (feof(str->data.file)) {
return KTX_FILE_UNEXPECTED_EOF;
} else {
return KTX_FILE_READ_ERROR;
}
}
}
str->readpos += count;
return KTX_SUCCESS;
}
/**
* @~English
* @brief Write bytes to a ktxFileStream.
*
* @param [in] str pointer to the ktxStream that is the destination of the
* write.
* @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_INVALID_VALUE @p str is @c NULL or @p src is @c NULL.
* @exception KTX_FILE_OVERFLOW the requested write would caused the file to
* exceed the maximum supported file size.
* @exception KTX_FILE_WRITE_ERROR a system error occurred while writing the
* file.
*/
static
KTX_error_code ktxFileStream_write(ktxStream* str, const void *src,
const ktx_size_t size,
const ktx_size_t count)
{
if (!str || !src)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeFile);
if (fwrite(src, size, count, str->data.file) != count) {
if (errno == EFBIG || errno == EOVERFLOW)
return KTX_FILE_OVERFLOW;
else
return KTX_FILE_WRITE_ERROR;
}
return KTX_SUCCESS;
}
/**
* @~English
* @brief Get the current read/write position in a ktxFileStream.
*
* @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_FILE_ISPIPE file descriptor underlying stream is associated
* with a pipe or FIFO so does not have a
* file-position indicator.
* @exception KTX_INVALID_VALUE @p str or @p pos is @c NULL.
*/
static
KTX_error_code ktxFileStream_getpos(ktxStream* str, ktx_off_t* pos)
{
ktx_off_t ftellval;
if (!str || !pos)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeFile);
if (str->data.file == stdin) {
*pos = str->readpos;
} else {
/* The cast quiets an Xcode warning when building for "Generic iOS Device".
* I'm not sure why.
*/
ftellval = (ktx_off_t)ftello(str->data.file);
if (ftellval < 0) {
switch (errno) {
case ESPIPE: return KTX_FILE_ISPIPE;
case EOVERFLOW: return KTX_FILE_OVERFLOW;
}
}
*pos = ftellval;
}
return KTX_SUCCESS;
}
/**
* @~English
* @brief Set the current read/write position in a ktxFileStream.
*
* Offset of 0 is the start of the file. This function operates
* like Linux > 3.1's @c lseek() when it is passed a @c whence
* of @c SEEK_DATA as it returns an error if the seek would
* go beyond the end 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.
*
* Throws the same exceptions as ktxFileStream_getsize() for the reasons given
* there plus the following:
*
* @exception KTX_INVALID_VALUE @p str is @c NULL.
* @exception KTX_INVALID_OPERATION @p pos is > the size of the file or an
* fseek error occurred.
*/
static
KTX_error_code ktxFileStream_setpos(ktxStream* str, ktx_off_t pos)
{
ktx_size_t fileSize;
KTX_error_code result;
if (!str)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeFile);
if (str->data.file == stdin) {
if (pos > str->readpos)
return str->skip(str, pos - str->readpos);
else
return KTX_FILE_ISPIPE;
}
result = str->getsize(str, &fileSize);
if (result != KTX_SUCCESS) {
// Device is likely not seekable.
return result;
}
if (pos > (ktx_off_t)fileSize)
return KTX_INVALID_OPERATION;
if (fseeko(str->data.file, pos, SEEK_SET) < 0)
return KTX_FILE_SEEK_ERROR;
else
return KTX_SUCCESS;
}
/**
* @~English
* @brief Get the size of a ktxFileStream 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_FILE_OVERFLOW size is too large to be returned in a
* @c ktx_size_t.
* @exception KTX_FILE_ISPIPE file descriptor underlying stream is associated
* with a pipe or FIFO so does not have a
* file-position indicator.
* @exception KTX_FILE_READ_ERROR a system error occurred while getting the
* size.
* @exception KTX_INVALID_VALUE @p str or @p size is @c NULL.
* @exception KTX_INVALID_OPERATION stream is a tty.
*/
static
KTX_error_code ktxFileStream_getsize(ktxStream* str, ktx_size_t* size)
{
struct stat statbuf;
int statret;
if (!str || !size)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeFile);
// Need to flush so that fstat will return the current size.
// Can ignore return value. The only error that can happen is to tell you
// it was a NOP because the file is read only.
#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(__MINGW64__) && !defined(_UCRT)
// Bug in VS2013 msvcrt. fflush on FILE open for READ changes file offset
// to 4096.
if (str->data.file->_flag & _IOWRT)
(void)fflush(str->data.file);
#else
(void)fflush(str->data.file);
#endif
statret = fstat(fileno(str->data.file), &statbuf);
if (statret < 0) {
switch (errno) {
case EOVERFLOW: return KTX_FILE_OVERFLOW;
case EIO:
default:
return KTX_FILE_READ_ERROR;
}
}
mode_t ftype = statbuf.st_mode & S_IFMT;
if (ftype == S_IFIFO || ftype == S_IFSOCK)
return KTX_FILE_ISPIPE;
if (statbuf.st_mode & S_IFCHR)
return KTX_INVALID_OPERATION;
*size = (ktx_size_t)statbuf.st_size; /* See _getpos for why this cast. */
return KTX_SUCCESS;
}
/**
* @~English
* @brief Initialize a ktxFileStream.
*
* @param [in] str pointer to the ktxStream to initialize.
* @param [in] file pointer to the underlying FILE object.
* @param [in] closeFileOnDestruct if not false, stdio file pointer will be closed when ktxStream
* is destructed.
*
* @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error.
*
* @exception KTX_INVALID_VALUE @p stream is @c NULL or @p file is @c NULL.
*/
KTX_error_code ktxFileStream_construct(ktxStream* str, FILE* file,
ktx_bool_t closeFileOnDestruct)
{
if (!str || !file)
return KTX_INVALID_VALUE;
str->data.file = file;
str->readpos = 0;
str->type = eStreamTypeFile;
str->read = ktxFileStream_read;
str->skip = ktxFileStream_skip;
str->write = ktxFileStream_write;
str->getpos = ktxFileStream_getpos;
str->setpos = ktxFileStream_setpos;
str->getsize = ktxFileStream_getsize;
str->destruct = ktxFileStream_destruct;
str->closeOnDestruct = closeFileOnDestruct;
return KTX_SUCCESS;
}
/**
* @~English
* @brief Destruct the stream, potentially closing the underlying FILE.
*
* This only closes the underyling FILE if the @c closeOnDestruct parameter to
* ktxFileStream_construct() was not @c KTX_FALSE.
*
* @param [in] str pointer to the ktxStream whose FILE is to potentially
* be closed.
*/
void
ktxFileStream_destruct(ktxStream* str)
{
assert(str && str->type == eStreamTypeFile);
if (str->closeOnDestruct)
fclose(str->data.file);
str->data.file = 0;
}
+27
View File
@@ -0,0 +1,27 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2010-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Author: Maksim Kolesin from original code
* by Mark Callow and Georg Kolling
*/
#ifndef FILESTREAM_H
#define FILESTREAM_H
#include "ktx.h"
/*
* ktxFileInit: Initialize a ktxStream to a ktxFileStream with a FILE object
*/
KTX_error_code ktxFileStream_construct(ktxStream* str, FILE* file,
ktx_bool_t closeFileOnDestruct);
void ktxFileStream_destruct(ktxStream* str);
#endif /* FILESTREAM_H */
+59
View File
@@ -0,0 +1,59 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2019-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Struct for returning size information about an image format.
*
* @author Mark Callow, github.com/MarkCallow
*/
#ifndef _FORMATSIZE_H_
#define _FORMATSIZE_H_
#include "ktx.h"
typedef enum ktxFormatSizeFlagBits {
KTX_FORMAT_SIZE_PACKED_BIT = 0x00000001,
KTX_FORMAT_SIZE_COMPRESSED_BIT = 0x00000002,
KTX_FORMAT_SIZE_PALETTIZED_BIT = 0x00000004,
KTX_FORMAT_SIZE_DEPTH_BIT = 0x00000008,
KTX_FORMAT_SIZE_STENCIL_BIT = 0x00000010,
KTX_FORMAT_SIZE_YUVSDA_BIT = 0x00000020,
} ktxFormatSizeFlagBits;
typedef ktx_uint32_t ktxFormatSizeFlags;
/**
* @brief Structure for holding size information for a texture format.
*/
typedef struct ktxFormatSize {
ktxFormatSizeFlags flags;
unsigned int paletteSizeInBits; // For KTX1.
unsigned int blockSizeInBits;
unsigned int blockWidth; // in texels
unsigned int blockHeight; // in texels
unsigned int blockDepth; // in texels
unsigned int minBlocksX; // Minimum required number of blocks
unsigned int minBlocksY;
} ktxFormatSize;
#ifdef __cplusplus
extern "C" {
#endif
bool ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* _FORMATSIZE_H_ */
+2438
View File
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2017-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief List of OpenGL {,ES} functions used by libktx.
*/
// There is no way for the pre-processor to uppercase stringized macro args
// so we have to explicitly give the types.
#define required 1 // Present in all GL versions. Load failure is an error.
#define not_required 0 // May not be present. Code must check before calling.
GL_FUNCTION(PFNGLBINDTEXTUREPROC, glBindTexture, required)
GL_FUNCTION(PFNGLCOMPRESSEDTEXIMAGE1DPROC, glCompressedTexImage1D, not_required)
GL_FUNCTION(PFNGLCOMPRESSEDTEXIMAGE2DPROC, glCompressedTexImage2D, required)
GL_FUNCTION(PFNGLCOMPRESSEDTEXIMAGE3DPROC, glCompressedTexImage3D, not_required)
GL_FUNCTION(PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC, glCompressedTexSubImage1D, not_required)
GL_FUNCTION(PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC, glCompressedTexSubImage2D, required)
GL_FUNCTION(PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC, glCompressedTexSubImage3D, not_required)
GL_FUNCTION(PFNGLDELETETEXTURESPROC, glDeleteTextures, required)
GL_FUNCTION(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap, not_required)
GL_FUNCTION(PFNGLGENTEXTURESPROC, glGenTextures, required)
GL_FUNCTION(PFNGLGETERRORPROC, glGetError, required)
GL_FUNCTION(PFNGLGETINTEGERVPROC, glGetIntegerv, required)
GL_FUNCTION(PFNGLGETSTRINGPROC, glGetString, required)
GL_FUNCTION(PFNGLGETSTRINGIPROC, glGetStringi, not_required)
GL_FUNCTION(PFNGLPIXELSTOREIPROC, glPixelStorei, required)
GL_FUNCTION(PFNGLTEXIMAGE1DPROC, glTexImage1D, not_required)
GL_FUNCTION(PFNGLTEXIMAGE2DPROC, glTexImage2D, required)
GL_FUNCTION(PFNGLTEXIMAGE3DPROC, glTexImage3D, not_required)
GL_FUNCTION(PFNGLTEXPARAMETERIPROC, glTexParameteri, required)
GL_FUNCTION(PFNGLTEXPARAMETERIVPROC, glTexParameteriv, required)
GL_FUNCTION(PFNGLTEXSTORAGE1DPROC, glTexStorage1D, not_required)
GL_FUNCTION(PFNGLTEXSTORAGE2DPROC, glTexStorage2D, not_required)
GL_FUNCTION(PFNGLTEXSTORAGE3DPROC, glTexStorage3D, not_required)
GL_FUNCTION(PFNGLTEXSUBIMAGE1DPROC, glTexSubImage1D, not_required)
GL_FUNCTION(PFNGLTEXSUBIMAGE2DPROC, glTexSubImage2D, required)
GL_FUNCTION(PFNGLTEXSUBIMAGE3DPROC, glTexSubImage3D, not_required)
#undef required
#undef not_required
+238
View File
@@ -0,0 +1,238 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2017-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Retrieve OpenGL function pointers needed by libktx
*/
#define UNIX 0
#define MACOS 0
#define WINDOWS 0
#define IOS 0
#if defined(_WIN32)
#undef WINDOWS
#define WINDOWS 1
#endif
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__)
#undef UNIX
#define UNIX 1
#endif
#if defined(linux) || defined(__linux) || defined(__linux__)
#undef UNIX
#define UNIX 1
#endif
#if defined(__APPLE__) && defined(__x86_64__)
#undef MACOS
#define MACOS 1
#endif
#if defined(__APPLE__) && (defined(__arm64__) || defined (__arm__))
#undef IOS
#define IOS 1
#endif
#if defined(__EMSCRIPTEN__)
#define WEB 1
#endif
#if (IOS + MACOS + UNIX + WINDOWS + WEB) > 1
#error "Multiple OS\'s defined"
#endif
#if WINDOWS
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#else
#include <dlfcn.h>
#include <stdlib.h>
#endif
#define NO_SHORTCUTS
#include "gl_funcs.h"
#if WINDOWS
#define GetOpenGLModuleHandle(flags) ktxFindOpenGL()
static HMODULE ktxOpenGLModuleHandle;
static PFNGLGETPROCADDRESS pfnWglGetProcAddress;
PFNVOIDFUNCTION
defaultGLGetProcAddress(const char* proc)
{
PFNVOIDFUNCTION pfnGLProc = NULL;
if (pfnWglGetProcAddress)
pfnGLProc = pfnWglGetProcAddress(proc);
if (!pfnGLProc) {
pfnGLProc = (PFNVOIDFUNCTION)GetProcAddress(ktxOpenGLModuleHandle,
proc);
}
return pfnGLProc;
}
#elif MACOS || UNIX || IOS
// Using NULL returns a handle that can be used to search the process that
// loaded us and any other libraries it has loaded. That's all we need to
// search as the app is responsible for creating the GL context so it must
// be there.
#define GetOpenGLModuleHandle(flags) dlopen(NULL, flags)
static void* ktxOpenGLModuleHandle;
PFNVOIDFUNCTION
defaultGLGetProcAddress(const char* proc)
{
return dlsym(ktxOpenGLModuleHandle, proc);
}
#elif WEB
extern void* emscripten_GetProcAddress(const char *name_);
#define GetOpenGLModuleHandle(flag) (void*)0x0000ffff // Value doesn't matter.
void* ktxOpenGLModuleHandle;
#define defaultGLGetProcAddress ((PFNGLGETPROCADDRESS)emscripten_GetProcAddress)
#else
#error "Don\'t know how to load symbols on this OS."
#endif
static bool openGLLoaded = false;
static const char* noloadmsg = "Could not load OpenGL command: %s!\n";
/* Define pointers for functions libktx is using. */
struct glFuncPtrs gl;
#define GL_FUNCTION(type, func, required) \
gl.func = (type)pfnGLGetProcAddress(#func); \
if ( !gl.func && required) { \
fprintf(stderr, noloadmsg, #func); \
return KTX_NOT_FOUND; \
}
#if WINDOWS
static HMODULE
ktxFindOpenGL() {
HMODULE module = 0;
BOOL found;
// Use GetModule not LoadLibrary so we only succeed if the process
// has already loaded some OpenGL library.
// Check current module to see if we are statically linked to GL.
found = GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCSTR)ktxFindOpenGL,
&module
);
if (found) {
if (GetProcAddress(module, "glGetError") != NULL)
return module;
}
// Not statically linked. See what dll the process has loaded.
// Emulators probably also have opengl32.lib loaded so check that last.
found = GetModuleHandleExA(
0,
"libGLESv2.dll",
&module
);
if (found) return module;
found = GetModuleHandleExA(
0,
"libGLES_CM.dll",
&module
);
if (found) return module;
found = GetModuleHandleExA(
0,
"opengl32.dll",
&module
);
if (found) {
// Need wglGetProcAddr for non-OpenGL-2 functions.
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-function-type-mismatch"
#endif
pfnWglGetProcAddress =
(PFNGLGETPROCADDRESS)GetProcAddress(module,
"wglGetProcAddress");
#ifdef __clang__
#pragma clang diagnostic pop
#endif
if (pfnWglGetProcAddress != NULL)
return module;
}
return 0;
}
#endif
ktx_error_code_e
ktxLoadOpenGLLibrary(void)
{
if (openGLLoaded)
return KTX_SUCCESS;
// Look for OpenGL module and set up default GetProcAddress.
ktxOpenGLModuleHandle = GetOpenGLModuleHandle(RTLD_LAZY);
if (ktxOpenGLModuleHandle == NULL) {
fprintf(stderr, "OpenGL lib not linked or loaded by application.\n");
// Normal use is for this constructor to be called by an
// application that has completed OpenGL initialization. In that
// case the only cause for failure would be a coding error in our
// library loading. The only other cause would be an application
// calling GLUpload without having initialized OpenGL.
#if defined(DEBUG)
abort();
#else
return KTX_LIBRARY_NOT_LINKED; // So release version doesn't crash.
#endif
}
return KTX_SUCCESS;
}
/**
* @ingroup ktx\_glloader
* @~English
* @brief Load pointers for the GL functions used by the ktxTexture\*\_GLUpload functions.
*
* Should be called by an application before its first call to a
* ktxTexture\*\_GLUpload function, passing a pointer to the GLGetProcAddress function
* provided by whatever OpenGL framework it is using. For backward
* compatibility, the ktxTexture\*\_GLUpload functions call this with a NULL pointer causing an
* attempt to load the pointers from the program module using
* @c dlsym (GNU/Linux, macOS), @c wglGetProcAddr and @c GetProcAddr (Windows)
* or @c emscripten_GetProcAddress (Web). This works with the vast majority of
* OpenGL implementations but issues have been seen on Fedora systems
* particularly with NVIDIA hardware. For full robustness, applications should
* call this function.
*
* @param [in] pfnGLGetProcAddress pointer to function for retrieving pointers
* to GL functions. If NULL, retrieval is
* attempted using system dependent generic
* functions.
*/
ktx_error_code_e
ktxLoadOpenGL(PFNGLGETPROCADDRESS pfnGLGetProcAddress)
{
if (openGLLoaded)
return KTX_SUCCESS;
if (!pfnGLGetProcAddress) {
ktx_error_code_e result = ktxLoadOpenGLLibrary();
if (result != KTX_SUCCESS) {
return result;
}
pfnGLGetProcAddress = defaultGLGetProcAddress;
}
// Load function pointers
#include "gl_funclist.inl"
openGLLoaded = true;
return KTX_SUCCESS;
}
#undef GL_FUNCTION
+56
View File
@@ -0,0 +1,56 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2017-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Declare pointers for OpenGL {,ES} functions.
*
* Dynamically retrieving pointers avoids apps or shared library builds having to link with OpenGL {,ES}
* and avoids the need for compiling different versions of the libary for different OpenGL {,ES} versions.
*/
#ifndef _GL_FUNCS_H_
#define _GL_FUNCS_H_
#undef GL_GLEXT_PROTOTYPES // Just to be sure.
#include "GL/glcorearb.h"
#include "ktx.h"
extern ktx_error_code_e ktxLoadOpenGLLibrary(void);
#define GL_FUNCTION(type, fun, required) type fun;
extern struct glFuncPtrs {
#include "gl_funclist.inl"
} gl;
#undef GL_FUNCTION
#if !defined(NO_SHORTCUTS)
// Macros to allow standard, i.e always present functions, to be called
// by their prototype names.
#define glBindTexture gl.glBindTexture
#define glCompressedTexImage2D gl.glCompressedTexImage2D
#define glCompressedTexSubImage2D gl.glCompressedTexSubImage2D
#define glDeleteTextures gl.glDeleteTextures
#define glGenTextures gl.glGenTextures
#define glGetError gl.glGetError
#define glGetIntegerv gl.glGetIntegerv
#define glGetString(x) (const char*)gl.glGetString(x)
#define glPixelStorei gl.glPixelStorei
#define glTexImage2D gl.glTexImage2D
#define glTexSubImage2D gl.glTexSubImage2D
#define glTexParameteri gl.glTexParameteri
#define glTexParameteriv gl.glTexParameteriv
#endif
#endif /* _GL_FUNCS_H_ */
+285
View File
@@ -0,0 +1,285 @@
/*
** Copyright 2025 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <ctype.h>
#include <stdio.h>
#include "GL/glcorearb.h"
#include "GL/glext.h"
#define GL_APIENTRY APIENTRY // Keep following included file happy.
#include "GLES2/gl2ext.h" // For the ASTC 3D values
// The unsuffixed names are deprecated so not in glcorearb.h and it defines macros
// which hide their definitions in glext.h. Rather than include gl.h define these in
// terms of the equivalent extension names which are also in glext.h.
#define GL_SR8 GL_SR8_EXT
#define GL_SRG8 GL_SRG8_EXT
// These are from GLES2/gl2.h.
#define GL_LUMINANCE 0x1909
#define GL_LUMINANCE_ALPHA 0x190A
// s/\(^[A-Z]\)/GL_\1/
// s/\(GL_[A-Z0-9_]*\) .*$/ case \1: return "\1";/
const char* glFormatString(GLenum format)
{
static char retstr[11];
switch (format) {
case 0: return "0 (Compressed)";
case GL_DEPTH_COMPONENT: return "GL_DEPTH_COMPONENT";
case GL_DEPTH_STENCIL: return "GL_DEPTH_STENCIL";
case GL_STENCIL_INDEX: return "GL_STENCIL_INDEX";
case GL_RED: return "GL_RED";
case GL_RG: return "GL_RG";
case GL_RGB: return "GL_RGB";
case GL_RGBA: return "GL_RGBA";
case GL_GREEN: return "GL_GREEN";
case GL_BLUE: return "GL_BLUE";
case GL_BGR: return "GL_BGR";
case GL_BGRA: return "GL_BGRA";
case GL_RED_INTEGER: return "GL_RED_INTEGER";
case GL_GREEN_INTEGER: return "GL_GREEN_INTEGER";
case GL_BLUE_INTEGER: return "GL_BLUE_INTEGER";
case GL_RG_INTEGER: return "GL_RG_INTEGER";
case GL_RGB_INTEGER: return "GL_RGB_INTEGER";
case GL_RGBA_INTEGER: return "GL_RGBA_INTEGER";
case GL_BGR_INTEGER: return "GL_BGR_INTEGER";
case GL_BGRA_INTEGER: return "GL_BGRA_INTEGER";
case GL_SRGB: return "GL_SRGB";
case GL_SRGB_ALPHA: return "GL_SRGB_ALPHA";
// Deprecated values.
case GL_ALPHA: return "GL_ALPHA";
case GL_LUMINANCE: return "GL_LUMINANCE";
case GL_LUMINANCE_ALPHA: return "GL_LUMINANCE_ALPHA";
default:
(void)snprintf(retstr, sizeof(retstr), "%#x", format);
return retstr;
}
}
const char* glInternalformatString(GLenum format)
{
static char retstr[11];
switch (format) {
case GL_R8: return "GL_R8";
case GL_R8_SNORM: return "GL_R8_SNORM";
case GL_R16: return "GL_R16";
case GL_R16_SNORM: return "GL_R16_SNORM";
case GL_RG8: return "GL_RG8";
case GL_RG8_SNORM: return "GL_RG8_SNORM";
case GL_RG16: return "GL_RG16";
case GL_RG16_SNORM: return "GL_RG16_SNORM";
case GL_R3_G3_B2: return "GL_R3_G3_B2";
case GL_RGB4: return "GL_RGB4";
case GL_RGB5: return "GL_RGB5";
case GL_RGB565: return "GL_RGB565";
case GL_RGB8: return "GL_RGB8";
case GL_RGB8_SNORM: return "GL_RGB8_SNORM";
case GL_RGB10: return "GL_RGB10";
case GL_RGB12: return "GL_RGB12";
case GL_RGB16: return "GL_RGB16";
case GL_RGB16_SNORM: return "GL_RGB16_SNORM";
case GL_RGBA2: return "GL_RGBA2";
case GL_RGBA4: return "GL_RGBA4";
case GL_RGB5_A1: return "GL_RGB5_A1";
case GL_RGBA8: return "GL_RGBA8";
case GL_RGBA8_SNORM: return "GL_RGBA8_SNORM";
case GL_RGB10_A2: return "GL_RGB10_A2";
case GL_RGB10_A2UI: return "GL_RGB10_A2UI";
case GL_RGBA12: return "GL_RGBA12";
case GL_RGBA16: return "GL_RGBA16";
case GL_RGBA16_SNORM: return "GL_RGBA16_SNORM";
case GL_SR8: return "GL_SR8";
case GL_SRG8: return "GL_SRG8";
case GL_SRGB8: return "GL_SRGB8";
case GL_SRGB8_ALPHA8: return "GL_SRGB8_ALPHA8";
case GL_R16F: return "GL_R16F";
case GL_RG16F: return "GL_RG16F";
case GL_RGB16F: return "GL_RGB16F";
case GL_RGBA16F: return "GL_RGBA16F";
case GL_R32F: return "GL_R32F";
case GL_RG32F: return "GL_RG32F";
case GL_RGB32F: return "GL_RGB32F";
case GL_RGBA32F: return "GL_RGBA32F";
case GL_R11F_G11F_B10F: return "GL_R11F_G11F_B10F";
case GL_RGB9_E5: return "GL_RGB9_E5";
case GL_R8I: return "GL_R8I";
case GL_R8UI: return "GL_R8UI";
case GL_R16I: return "GL_R16I";
case GL_R16UI: return "GL_R16UI";
case GL_R32I: return "GL_R32I";
case GL_R32UI: return "GL_R32UI";
case GL_RG8I: return "GL_RG8I";
case GL_RG8UI: return "GL_RG8UI";
case GL_RG16I: return "GL_RG16I";
case GL_RG16UI: return "GL_RG16UI";
case GL_RG32I: return "GL_RG32I";
case GL_RG32UI: return "GL_RG32UI";
case GL_RGB8I: return "GL_RGB8I";
case GL_RGB8UI: return "GL_RGB8UI";
case GL_RGB16I: return "GL_RGB16I";
case GL_RGB16UI: return "GL_RGB16UI";
case GL_RGB32I: return "GL_RGB32I";
case GL_RGB32UI: return "GL_RGB32UI";
case GL_RGBA8I: return "GL_RGBA8I";
case GL_RGBA8UI: return "GL_RGBA8UI";
case GL_RGBA16I: return "GL_RGBA16I";
case GL_RGBA16UI: return "GL_RGBA16UI";
case GL_RGBA32I: return "GL_RGBA32I";
case GL_RGBA32UI: return "GL_RGBA32UI";
case GL_DEPTH_COMPONENT16: return "GL_DEPTH_COMPONENT16";
case GL_DEPTH_COMPONENT24: return "GL_DEPTH_COMPONENT24";
case GL_DEPTH_COMPONENT32: return "GL_DEPTH_COMPONENT32";
case GL_DEPTH_COMPONENT32F: return "GL_DEPTH_COMPONENT32F";
case GL_DEPTH24_STENCIL8: return "GL_DEPTH24_STENCIL8";
case GL_DEPTH32F_STENCIL8: return "GL_DEPTH32F_STENCIL8";
case GL_STENCIL_INDEX1: return "GL_STENCIL_INDEX1";
case GL_STENCIL_INDEX4: return "GL_STENCIL_INDEX4";
case GL_STENCIL_INDEX8: return "GL_STENCIL_INDEX8";
case GL_STENCIL_INDEX16: return "GL_STENCIL_INDEX16";
case GL_COMPRESSED_RED: return "GL_COMPRESSED_RED";
case GL_COMPRESSED_RG: return "GL_COMPRESSED_RG";
case GL_COMPRESSED_RGB: return "GL_COMPRESSED_RGB";
case GL_COMPRESSED_RGBA: return "GL_COMPRESSED_RGBA";
case GL_COMPRESSED_SRGB: return "GL_COMPRESSED_SRGB";
case GL_COMPRESSED_SRGB_ALPHA: return "GL_COMPRESSED_SRGB_ALPHA";
case GL_COMPRESSED_RED_RGTC1: return "GL_COMPRESSED_RED_RGTC1";
case GL_COMPRESSED_SIGNED_RED_RGTC1: return "GL_COMPRESSED_SIGNED_RED_RGTC1";
case GL_COMPRESSED_RG_RGTC2: return "GL_COMPRESSED_RG_RGTC2";
case GL_COMPRESSED_SIGNED_RG_RGTC2: return "GL_COMPRESSED_SIGNED_RG_RGTC2";
case GL_COMPRESSED_RGBA_BPTC_UNORM: return "GL_COMPRESSED_RGBA_BPTC_UNORM";
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: return "GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM";
case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: return "GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT";
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: return "GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT";
case GL_COMPRESSED_RGB8_ETC2: return "GL_COMPRESSED_RGB8_ETC2";
case GL_COMPRESSED_SRGB8_ETC2: return "GL_COMPRESSED_SRGB8_ETC2";
case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: return "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2";
case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2";
case GL_COMPRESSED_RGBA8_ETC2_EAC: return "GL_COMPRESSED_RGBA8_ETC2_EAC";
case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC";
case GL_COMPRESSED_R11_EAC: return "GL_COMPRESSED_R11_EAC";
case GL_COMPRESSED_SIGNED_R11_EAC: return "GL_COMPRESSED_SIGNED_R11_EAC";
case GL_COMPRESSED_RG11_EAC: return "GL_COMPRESSED_RG11_EAC";
case GL_COMPRESSED_SIGNED_RG11_EAC: return "GL_COMPRESSED_SIGNED_RG11_EAC";
case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: return "GL_COMPRESSED_RGBA_ASTC_4x4_KHR";
case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: return "GL_COMPRESSED_RGBA_ASTC_5x4_KHR";
case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: return "GL_COMPRESSED_RGBA_ASTC_5x5_KHR";
case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: return "GL_COMPRESSED_RGBA_ASTC_6x5_KHR";
case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: return "GL_COMPRESSED_RGBA_ASTC_6x6_KHR";
case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: return "GL_COMPRESSED_RGBA_ASTC_8x5_KHR";
case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: return "GL_COMPRESSED_RGBA_ASTC_8x6_KHR";
case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: return "GL_COMPRESSED_RGBA_ASTC_8x8_KHR";
case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: return "GL_COMPRESSED_RGBA_ASTC_10x5_KHR";
case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: return "GL_COMPRESSED_RGBA_ASTC_10x6_KHR";
case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: return "GL_COMPRESSED_RGBA_ASTC_10x8_KHR";
case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: return "GL_COMPRESSED_RGBA_ASTC_10x10_KHR";
case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: return "GL_COMPRESSED_RGBA_ASTC_12x10_KHR";
case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: return "GL_COMPRESSED_RGBA_ASTC_12x12_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR";
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return "GL_COMPRESSED_RGB_S3TC_DXT1_EXT";
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT";
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT";
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT";
case GL_COMPRESSED_RGB_FXT1_3DFX: return "GL_COMPRESSED_RGB_FXT1_3DFX";
case GL_COMPRESSED_RGBA_FXT1_3DFX: return "GL_COMPRESSED_RGBA_FXT1_3DFX";
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: return "GL_COMPRESSED_SRGB_S3TC_DXT1_EXT";
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT";
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT";
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT";
case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: return "GL_COMPRESSED_RGBA_ASTC_3x3x3_OES";
case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: return "GL_COMPRESSED_RGBA_ASTC_4x3x3_OES";
case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: return "GL_COMPRESSED_RGBA_ASTC_4x4x3_OES";
case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: return "GL_COMPRESSED_RGBA_ASTC_4x4x4_OES";
case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: return "GL_COMPRESSED_RGBA_ASTC_5x4x4_OES";
case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: return "GL_COMPRESSED_RGBA_ASTC_5x5x4_OES";
case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: return "GL_COMPRESSED_RGBA_ASTC_5x5x5_OES";
case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: return "GL_COMPRESSED_RGBA_ASTC_6x5x5_OES";
case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: return "GL_COMPRESSED_RGBA_ASTC_6x6x5_OES";
case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: return "GL_COMPRESSED_RGBA_ASTC_6x6x6_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES";
case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES";
case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: return "GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG";
case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: return "GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG";
case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: return "GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG";
case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: return "GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG";
case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: return "GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT";
case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: return "GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT";
case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: return "GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT";
case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: return "GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT";
case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: return "GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG";
case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: return "GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG";
case GL_ETC1_RGB8_OES: return "GL_ETC1_RGB8_OES";
// Deprecated values.
case GL_ALPHA8_EXT: return "GL_ALPHA8_EXT";
case GL_LUMINANCE4_ALPHA4_EXT: return "GL_LUMINANCE4_ALPHA4_EXT";
case GL_LUMINANCE8_ALPHA8_EXT: return "GL_LUMINANCE8_ALPHA8_EXT";
case GL_LUMINANCE8_EXT: return "GL_LUMINANCE8_EXT";
default:
(void)snprintf(retstr, sizeof(retstr), "%#x", format);
return retstr;
}
}
const char* glTypeString(GLenum type)
{
static char retstr[11];
switch (type) {
case 0: return "0 (Compressed)";
case GL_UNSIGNED_BYTE: return "GL_UNSIGNED_BYTE";
case GL_BYTE: return "GL_BYTE";
case GL_UNSIGNED_SHORT: return "GL_UNSIGNED_SHORT";
case GL_SHORT: return "GL_SHORT";
case GL_UNSIGNED_INT: return "GL_UNSIGNED_INT";
case GL_INT: return "GL_INT";
case GL_HALF_FLOAT: return "GL_HALF_FLOAT";
case GL_FLOAT: return "GL_FLOAT";
case GL_UNSIGNED_BYTE_3_3_2: return "GL_UNSIGNED_BYTE_3_3_2";
case GL_UNSIGNED_BYTE_2_3_3_REV: return "GL_UNSIGNED_BYTE_2_3_3_REV";
case GL_UNSIGNED_SHORT_5_6_5: return "GL_UNSIGNED_SHORT_5_6_5";
case GL_UNSIGNED_SHORT_5_6_5_REV: return "GL_UNSIGNED_SHORT_5_6_5_REV";
case GL_UNSIGNED_SHORT_4_4_4_4: return "GL_UNSIGNED_SHORT_4_4_4_4";
case GL_UNSIGNED_SHORT_4_4_4_4_REV: return "GL_UNSIGNED_SHORT_4_4_4_4_REV";
case GL_UNSIGNED_SHORT_5_5_5_1: return "GL_UNSIGNED_SHORT_5_5_5_1";
case GL_UNSIGNED_SHORT_1_5_5_5_REV: return "GL_UNSIGNED_SHORT_1_5_5_5_REV";
case GL_UNSIGNED_INT_8_8_8_8: return "GL_UNSIGNED_INT_8_8_8_8";
case GL_UNSIGNED_INT_8_8_8_8_REV: return "GL_UNSIGNED_INT_8_8_8_8_REV";
case GL_UNSIGNED_INT_10_10_10_2: return "GL_UNSIGNED_INT_10_10_10_2";
case GL_UNSIGNED_INT_2_10_10_10_REV: return "GL_UNSIGNED_INT_2_10_10_10_REV";
case GL_UNSIGNED_INT_24_8: return "GL_UNSIGNED_INT_24_8";
case GL_UNSIGNED_INT_10F_11F_11F_REV: return "GL_UNSIGNED_INT_10F_11F_11F_REV";
case GL_UNSIGNED_INT_5_9_9_9_REV: return "GL_UNSIGNED_INT_5_9_9_9_REV";
case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: return "GL_FLOAT_32_UNSIGNED_INT_24_8_REV";
default:
(void)snprintf(retstr, sizeof(retstr), "%#x", type);
return retstr;
}
}
+1087
View File
File diff suppressed because it is too large Load Diff
+628
View File
@@ -0,0 +1,628 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2010-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Functions for creating and using a hash list of key-value
* pairs.
*
* @author Mark Callow, HI Corporation
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
// This is to avoid compile warnings. strlen is defined as returning
// size_t and is used by the uthash macros. This avoids having to
// make changes to uthash and a bunch of casts in this file. The
// casts would be required because the key and value lengths in KTX
// are specified as 4 byte quantities so we can't change _keyAndValue
// below to use size_t.
#define strlen(x) ((unsigned int)strlen(x))
#include "uthash.h"
#include "ktx.h"
#include "ktxint.h"
/**
* @internal
* @struct ktxKVListEntry
* @brief Hash list entry structure
*/
typedef struct ktxKVListEntry {
unsigned int keyLen; /*!< Length of the key */
char* key; /*!< Pointer to key string */
unsigned int valueLen; /*!< Length of the value */
void* value; /*!< Pointer to the value */
UT_hash_handle hh; /*!< handle used by UT hash */
} ktxKVListEntry;
/**
* @memberof ktxHashList @public
* @~English
* @brief Construct an empty hash list for storing key-value pairs.
*
* @param [in] pHead pointer to the location to write the list head.
*/
void
ktxHashList_Construct(ktxHashList* pHead)
{
*pHead = NULL;
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Construct a hash list by copying another.
*
* @param [in] pHead pointer to head of the list.
* @param [in] orig head of the original hash list.
*/
void
ktxHashList_ConstructCopy(ktxHashList* pHead, ktxHashList orig)
{
ktxHashListEntry* entry = orig;
*pHead = NULL;
for (; entry != NULL; entry = ktxHashList_Next(entry)) {
(void)ktxHashList_AddKVPair(pHead,
entry->key, entry->valueLen, entry->value);
}
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Destruct a hash list.
*
* All memory associated with the hash list's keys and values
* is freed.
*
* @param [in] pHead pointer to the hash list to be destroyed.
*/
void
ktxHashList_Destruct(ktxHashList* pHead)
{
ktxKVListEntry* kv;
ktxKVListEntry* head = *pHead;
for(kv = head; kv != NULL;) {
ktxKVListEntry* tmp = (ktxKVListEntry*)kv->hh.next;
HASH_DELETE(hh, head, kv);
free(kv);
kv = tmp;
}
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Create an empty hash list for storing key-value pairs.
*
* @param [in,out] ppHl address of a variable in which to set a pointer to
* the newly created hash list.
*
* @return KTX_SUCCESS or one of the following error codes.
* @exception KTX_OUT_OF_MEMORY if not enough memory.
*/
KTX_error_code
ktxHashList_Create(ktxHashList** ppHl)
{
ktxHashList* hl = (ktxHashList*)malloc(sizeof (ktxKVListEntry*));
if (hl == NULL)
return KTX_OUT_OF_MEMORY;
ktxHashList_Construct(hl);
*ppHl = hl;
return KTX_SUCCESS;
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Create a copy of a hash list.
*
* @param [in,out] ppHl address of a variable in which to set a pointer to
* the newly created hash list.
* @param [in] orig head of the ktxHashList to copy.
*
* @return KTX_SUCCESS or one of the following error codes.
* @exception KTX_OUT_OF_MEMORY if not enough memory.
*/
KTX_error_code
ktxHashList_CreateCopy(ktxHashList** ppHl, ktxHashList orig)
{
ktxHashList* hl = (ktxHashList*)malloc(sizeof (ktxKVListEntry*));
if (hl == NULL)
return KTX_OUT_OF_MEMORY;
ktxHashList_ConstructCopy(hl, orig);
*ppHl = hl;
return KTX_SUCCESS;
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Destroy a hash list.
*
* All memory associated with the hash list's keys and values
* is freed. The hash list is also freed.
*
* @param [in] pHead pointer to the hash list to be destroyed.
*/
void
ktxHashList_Destroy(ktxHashList* pHead)
{
ktxHashList_Destruct(pHead);
free(pHead);
}
#if !__clang__ && __GNUC__ // Grumble clang grumble
// These are in uthash.h macros. I don't want to change that file.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif
/**
* @memberof ktxHashList @public
* @~English
* @brief Add a key value pair to a hash list.
*
* The value can be empty, i.e, its length can be 0.
*
* @param [in] pHead pointer to the head of the target hash list.
* @param [in] key pointer to the UTF8 NUL-terminated string to be used as the key.
* @param [in] valueLen the number of bytes of data in @p value.
* @param [in] value pointer to the bytes of data constituting the value.
*
* @return KTX_SUCCESS or one of the following error codes.
* @exception KTX_INVALID_VALUE if @p pHead, @p key or @p value are NULL, @p key is an
* empty string or @p valueLen == 0.
*/
KTX_error_code
ktxHashList_AddKVPair(ktxHashList* pHead, const char* key, unsigned int valueLen, const void* value)
{
if (pHead && key && (valueLen == 0 || value)) {
unsigned int keyLen = (unsigned int)strlen(key) + 1;
ktxKVListEntry* kv;
if (keyLen == 1)
return KTX_INVALID_VALUE; /* Empty string */
/* Allocate all the memory as a block */
kv = (ktxKVListEntry*)malloc(sizeof(ktxKVListEntry) + keyLen + valueLen);
/* Put key first */
kv->key = (char *)kv + sizeof(ktxKVListEntry);
kv->keyLen = keyLen;
memcpy(kv->key, key, keyLen);
/* then value */
kv->valueLen = valueLen;
if (valueLen > 0) {
kv->value = kv->key + keyLen;
memcpy(kv->value, value, valueLen);
} else {
kv->value = 0;
}
HASH_ADD_KEYPTR( hh, *pHead, kv->key, kv->keyLen-1, kv);
return KTX_SUCCESS;
} else
return KTX_INVALID_VALUE;
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Delete a key value pair in a hash list.
*
* Is a nop if the key is not in the hash.
*
* @param [in] pHead pointer to the head of the target hash list.
* @param [in] key pointer to the UTF8 NUL-terminated string to be used as the key.
*
* @return KTX_SUCCESS or one of the following error codes.
* @exception KTX_INVALID_VALUE if @p pHead is NULL or @p key is an empty
* string.
*/
KTX_error_code
ktxHashList_DeleteKVPair(ktxHashList* pHead, const char* key)
{
if (pHead && key) {
ktxKVListEntry* kv;
HASH_FIND_STR( *pHead, key, kv ); /* kv: pointer to target entry. */
if (kv != NULL)
HASH_DEL(*pHead, kv);
return KTX_SUCCESS;
} else
return KTX_INVALID_VALUE;
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Delete an entry from a hash list.
*
* @param [in] pHead pointer to the head of the target hash list.
* @param [in] pEntry pointer to the ktxHashListEntry to delete.
*
* @return KTX_SUCCESS or one of the following error codes.
* @exception KTX_INVALID_VALUE if @p pHead is NULL or @p key is an empty
* string.
*/
KTX_error_code
ktxHashList_DeleteEntry(ktxHashList* pHead, ktxHashListEntry* pEntry)
{
if (pHead && pEntry) {
HASH_DEL(*pHead, pEntry);
return KTX_SUCCESS;
} else
return KTX_INVALID_VALUE;
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Looks up a key in a hash list and returns the entry.
*
* @param [in] pHead pointer to the head of the target hash list.
* @param [in] key pointer to a UTF8 NUL-terminated string to find.
* @param [in,out] ppEntry @p *ppEntry is set to the point at the
* ktxHashListEntry.
*
* @return KTX_SUCCESS or one of the following error codes.
*
* @exception KTX_INVALID_VALUE if @p This, @p key or @p pValueLen or @p ppValue
* is NULL.
* @exception KTX_NOT_FOUND an entry matching @p key was not found.
*/
KTX_error_code
ktxHashList_FindEntry(ktxHashList* pHead, const char* key,
ktxHashListEntry** ppEntry)
{
if (pHead && key) {
ktxKVListEntry* kv;
HASH_FIND_STR( *pHead, key, kv ); /* kv: output pointer */
if (kv) {
*ppEntry = kv;
return KTX_SUCCESS;
} else
return KTX_NOT_FOUND;
} else
return KTX_INVALID_VALUE;
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Looks up a key in a hash list and returns the value.
*
* @param [in] pHead pointer to the head of the target hash list.
* @param [in] key pointer to a UTF8 NUL-terminated string to find.
* @param [in,out] pValueLen @p *pValueLen is set to the number of bytes of
* data in the returned value.
* @param [in,out] ppValue @p *ppValue is set to the point to the value for
* @p key.
*
* @return KTX_SUCCESS or one of the following error codes.
*
* @exception KTX_INVALID_VALUE if @p This, @p key or @p pValueLen or @p ppValue
* is NULL.
* @exception KTX_NOT_FOUND an entry matching @p key was not found.
*/
KTX_error_code
ktxHashList_FindValue(ktxHashList *pHead, const char* key, unsigned int* pValueLen, void** ppValue)
{
if (pValueLen && ppValue) {
ktxHashListEntry* pEntry;
KTX_error_code result;
result = ktxHashList_FindEntry(pHead, key, &pEntry);
if (result == KTX_SUCCESS) {
ktxHashListEntry_GetValue(pEntry, pValueLen, ppValue);
return KTX_SUCCESS;
} else
return result;
} else
return KTX_INVALID_VALUE;
}
#if !__clang__ && __GNUC__
#pragma GCC diagnostic pop
#endif
/**
* @memberof ktxHashList @public
* @~English
* @brief Returns the next entry in a ktxHashList.
*
* Use for iterating through the list:
* @code
* ktxHashListEntry* entry;
* for (entry = listHead; entry != NULL; entry = ktxHashList_Next(entry)) {
* ...
* };
* @endcode
*
* Note
*
* @param [in] entry pointer to a hash list entry. Note that a ktxHashList*,
* i.e. the list head, is also a pointer to an entry so
* can be passed to this function.
*
* @return a pointer to the next entry or NULL.
*
*/
ktxHashListEntry*
ktxHashList_Next(ktxHashListEntry* entry)
{
if (entry) {
return ((ktxKVListEntry*)entry)->hh.next;
} else
return NULL;
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Serialize a hash list to a block of data suitable for writing
* to a file.
*
* The caller is responsible for freeing the data block returned by this
* function.
*
* @param [in] pHead pointer to the head of the target hash list.
* @param [in,out] pKvdLen @p *pKvdLen is set to the number of bytes of
* data in the returned data block.
* @param [in,out] ppKvd @p *ppKvd is set to the point to the block of
* memory containing the serialized data or
* NULL. if the hash list is empty.
*
* @return KTX_SUCCESS or one of the following error codes.
*
* @exception KTX_INVALID_VALUE if @p This, @p pKvdLen or @p ppKvd is NULL.
* @exception KTX_OUT_OF_MEMORY there was not enough memory to serialize the
* data.
*/
KTX_error_code
ktxHashList_Serialize(ktxHashList* pHead,
unsigned int* pKvdLen, unsigned char** ppKvd)
{
if (pHead && pKvdLen && ppKvd) {
ktxKVListEntry* kv;
unsigned int bytesOfKeyValueData = 0;
unsigned int keyValueLen;
unsigned char* sd;
char padding[4] = {0, 0, 0, 0};
for (kv = *pHead; kv != NULL; kv = kv->hh.next) {
/* sizeof(sd) is to make space to write keyAndValueByteSize */
keyValueLen = kv->keyLen + kv->valueLen + sizeof(ktx_uint32_t);
/* Add valuePadding */
keyValueLen = _KTX_PAD4(keyValueLen);
bytesOfKeyValueData += keyValueLen;
}
if (bytesOfKeyValueData == 0) {
*pKvdLen = 0;
*ppKvd = NULL;
} else {
sd = malloc(bytesOfKeyValueData);
if (!sd)
return KTX_OUT_OF_MEMORY;
*pKvdLen = bytesOfKeyValueData;
*ppKvd = sd;
for (kv = *pHead; kv != NULL; kv = kv->hh.next) {
int padLen;
keyValueLen = kv->keyLen + kv->valueLen;
*(ktx_uint32_t*)sd = keyValueLen;
sd += sizeof(ktx_uint32_t);
memcpy(sd, kv->key, kv->keyLen);
sd += kv->keyLen;
if (kv->valueLen > 0)
memcpy(sd, kv->value, kv->valueLen);
sd += kv->valueLen;
padLen = _KTX_PAD4_LEN(keyValueLen);
memcpy(sd, padding, padLen);
sd += padLen;
}
}
return KTX_SUCCESS;
} else
return KTX_INVALID_VALUE;
}
int sort_by_key_codepoint(ktxKVListEntry* a, ktxKVListEntry* b) {
return strcmp(a->key, b->key);
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Sort a hash list in order of the UTF8 codepoints.
*
* @param [in] pHead pointer to the head of the target hash list.
*
* @return KTX_SUCCESS or one of the following error codes.
*
* @exception KTX_INVALID_VALUE if @p This is NULL.
*/
KTX_error_code
ktxHashList_Sort(ktxHashList* pHead)
{
if (pHead) {
//ktxKVListEntry* kv = (ktxKVListEntry*)pHead;
HASH_SORT(*pHead, sort_by_key_codepoint);
return KTX_SUCCESS;
} else {
return KTX_INVALID_VALUE;
}
}
/**
* @memberof ktxHashList @public
* @~English
* @brief Construct a hash list from a block of serialized key-value
* data read from a file.
* @note The bytes of the 32-bit key-value lengths within the serialized data
* are expected to be in native endianness.
*
* @param [in] pHead pointer to the head of the target hash list.
* @param [in] kvdLen the length of the serialized key-value data.
* @param [in] pKvd pointer to the serialized key-value data.
* table.
*
* @return KTX_SUCCESS or one of the following error codes.
*
* @exception KTX_INVALID_OPERATION if @p pHead does not point to an empty list.
* @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0.
* @exception KTX_OUT_OF_MEMORY there was not enough memory to create the hash
* table.
*/
KTX_error_code
ktxHashList_Deserialize(ktxHashList* pHead, unsigned int kvdLen, void* pKvd)
{
char* src = pKvd;
KTX_error_code result;
if (kvdLen == 0 || pKvd == NULL || pHead == NULL)
return KTX_INVALID_VALUE;
if (*pHead != NULL)
return KTX_INVALID_OPERATION;
result = KTX_SUCCESS;
while (result == KTX_SUCCESS && src < (char *)pKvd + kvdLen) {
if (src + 6 > (char *)pKvd + kvdLen) {
// Not enough space for another entry
return KTX_FILE_DATA_ERROR;
}
char* key;
unsigned int keyLen, valueLen;
void* value;
ktx_uint32_t keyAndValueByteSize = *((ktx_uint32_t*)src);
if (src + 4 + keyAndValueByteSize > (char *)pKvd + kvdLen) {
// Not enough space for this entry
return KTX_FILE_DATA_ERROR;
}
src += sizeof(keyAndValueByteSize);
key = src;
keyLen = 0;
while (keyLen < keyAndValueByteSize && key[keyLen] != '\0') keyLen++;
if (keyLen == keyAndValueByteSize || key[keyLen] != '\0') {
// Missing NULL terminator or no value
return KTX_FILE_DATA_ERROR;
}
if (keyLen >= 3 && key[0] == '\xEF' && key[1] == '\xBB' && key[2] == '\xBF') {
// Forbidden BOM
return KTX_FILE_DATA_ERROR;
}
keyLen += 1;
value = key + keyLen;
valueLen = keyAndValueByteSize - keyLen;
result = ktxHashList_AddKVPair(pHead, key, valueLen,
valueLen > 0 ? value : NULL);
if (result == KTX_SUCCESS) {
src += _KTX_PAD4(keyAndValueByteSize);
}
}
return result;
}
/**
* @memberof ktxHashListEntry @public
* @~English
* @brief Return the key of a ktxHashListEntry
*
* @param [in] This The target hash list entry.
* @param [in,out] pKeyLen @p *pKeyLen is set to the byte length of
* the returned key.
* @param [in,out] ppKey @p *ppKey is set to the point to the value of
* @p the key.
*
* @return KTX_SUCCESS or one of the following error codes.
*
* @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0.
*/
KTX_error_code
ktxHashListEntry_GetKey(ktxHashListEntry* This,
unsigned int* pKeyLen, char** ppKey)
{
if (pKeyLen && ppKey) {
ktxKVListEntry* kv = (ktxKVListEntry*)This;
*pKeyLen = kv->keyLen;
*ppKey = kv->key;
return KTX_SUCCESS;
} else
return KTX_INVALID_VALUE;
}
/**
* @memberof ktxHashListEntry @public
* @~English
* @brief Return the value from a ktxHashListEntry
*
* @param [in] This The target hash list entry.
* @param [in,out] pValueLen @p *pValueLen is set to the number of bytes of
* data in the returned value.
* @param [in,out] ppValue @p *ppValue is set to point to the value of
* of the target entry.
*
* @return KTX_SUCCESS or one of the following error codes.
*
* @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0.
*/
KTX_error_code
ktxHashListEntry_GetValue(ktxHashListEntry* This,
unsigned int* pValueLen, void** ppValue)
{
if (pValueLen && ppValue) {
ktxKVListEntry* kv = (ktxKVListEntry*)This;
*pValueLen = kv->valueLen;
*ppValue = kv->valueLen > 0 ? kv->value : NULL;
return KTX_SUCCESS;
} else
return KTX_INVALID_VALUE;
}
+1380
View File
File diff suppressed because it is too large Load Diff
+50
View File
@@ -0,0 +1,50 @@
; Copyright 2019-2020 Mark Callow
; SPDX-License-Identifier: Apache-2.0
; Export internal functions used by internal test programs and the
; validator. Using this file avoids having to decorate those internal
; functions or updating such decorations as the tests evolve. Updating
; this feels less obnoxious.
LIBRARY
EXPORTS
?basis_get_bytes_per_block_or_pixel@basist@@YAIW4transcoder_texture_format@1@@Z
?basis_transcoder_format_is_uncompressed@basist@@YA_NW4transcoder_texture_format@1@@Z
?basis_get_uncompressed_bytes_per_pixel@basist@@YAIW4transcoder_texture_format@1@@Z
?validate_header@basisu_transcoder@basist@@QEBA_NPEBXI@Z
?get_total_images@basisu_transcoder@basist@@QEBAIPEBXI@Z
?get_image_level_desc@basisu_transcoder@basist@@QEBA_NPEBXIIIAEAI11@Z
?get_image_info@basisu_transcoder@basist@@QEBA_NPEBXIAEAUbasisu_image_info@2@I@Z
?get_image_level_info@basisu_transcoder@basist@@QEBA_NPEBXIAEAUbasisu_image_level_info@2@II@Z
?start_transcoding@basisu_transcoder@basist@@QEAA_NPEBXI@Z
?transcode_image_level@basisu_transcoder@basist@@QEBA_NPEBXIIIPEAXIW4transcoder_texture_format@2@IIPEAUbasisu_transcoder_state@2@I@Z
createDFDCompressed
createDFDDepthStencil
createDFDUnpacked
createDFDPacked
findMapping
getPrimaries
interpretDFD
isProhibitedFormat
isValidFormat
ktxCheckHeader1_
ktxMemStream_construct
ktxMemStream_construct_ro
ktxMemStream_destruct
ktxMemStream_getdata
ktxTexture_calcImageSize
ktxTexture_calcLevelSize
ktxTexture1_Destroy
ktxTexture1_SetImageFromMemory
ktxTexture1_WriteToMemory
ktxTexture1_calcLevelOffset
ktxTexture1_destruct
ktxTexture1_glTypeSize
ktxTexture2_GetImageOffset
ktxTexture2_calcLevelOffset
ktxTexture2_destruct
reconstructDFDBytesPlanesFromSamples
stringToVkFormat
vk2dfd
vkFormatString
vkFormatTypeSize
+50
View File
@@ -0,0 +1,50 @@
; Copyright 2019-2020 Mark Callow
; SPDX-License-Identifier: Apache-2.0
; Export internal functions used by internal test programs and the
; validator. Using this file avoids having to decorate those internal
; functions or updating such decorations as the tests evolve. Updating
; this feels less obnoxious.
LIBRARY libktx
EXPORTS
_ZN6basist34basis_get_bytes_per_block_or_pixelENS_25transcoder_texture_formatE
_ZN6basist39basis_transcoder_format_is_uncompressedENS_25transcoder_texture_formatE
_ZN6basist38basis_get_uncompressed_bytes_per_pixelENS_25transcoder_texture_formatE
_ZNK6basist17basisu_transcoder15validate_headerEPKvj
_ZNK6basist17basisu_transcoder16get_total_imagesEPKvj
_ZNK6basist17basisu_transcoder20get_image_level_descEPKvjjjRjS3_S3_
_ZNK6basist17basisu_transcoder14get_image_infoEPKvjRNS_17basisu_image_infoEj
_ZNK6basist17basisu_transcoder20get_image_level_infoEPKvjRNS_23basisu_image_level_infoEjj
_ZN6basist17basisu_transcoder17start_transcodingEPKvj
_ZNK6basist17basisu_transcoder21transcode_image_levelEPKvjjjPvjNS_25transcoder_texture_formatEjjPNS_23basisu_transcoder_stateEj
createDFDCompressed
createDFDDepthStencil
createDFDUnpacked
createDFDPacked
findMapping
getPrimaries
interpretDFD
isProhibitedFormat
isValidFormat
ktxCheckHeader1_
ktxMemStream_construct
ktxMemStream_construct_ro
ktxMemStream_destruct
ktxMemStream_getdata
ktxTexture_calcImageSize
ktxTexture_calcLevelSize
ktxTexture1_Destroy
ktxTexture1_SetImageFromMemory
ktxTexture1_WriteToMemory
ktxTexture1_calcLevelOffset
ktxTexture1_destruct
ktxTexture1_glTypeSize
ktxTexture2_GetImageOffset
ktxTexture2_calcLevelOffset
ktxTexture2_destruct
reconstructDFDBytesPlanesFromSamples
stringToVkFormat
vk2dfd
vkFormatString
vkFormatTypeSize
+30
View File
@@ -0,0 +1,30 @@
; Copyright 2019-2020 Mark Callow
; SPDX-License-Identifier: Apache-2.0
; Export internal functions used by internal test programs and the
; validator. Using this file avoids having to decorate those internal
; functions or updating such decorations as the tests evolve. Updating
; this feels less obnoxious.
LIBRARY
EXPORTS
??1Resampler@basisu@@QEAA@XZ
??0Resampler@basisu@@QEAA@HHHHW4Boundary_Op@01@MMPEBDPEAUContrib_List@01@2MMMM@Z
?put_line@Resampler@basisu@@QEAA_NPEBM@Z
?get_line@Resampler@basisu@@QEAAPEBMXZ
?calc@image_metrics@basisu@@QEAAXAEBVimage@2@0II_N1@Z
?compute_ssim@basisu@@YA?AV?$vec@$03M@1@AEBVimage@1@0_N1@Z
?increase_capacity@elemental_vector@basisu@@QEAA_NI_NIP6AXPEAX1I@Z0@Z
?swizzle_to_rgba@@YAXPEAE0I_KQEAW4swizzle_e@@@Z
appendLibId
dfdToStringChannelId
dfdToStringColorModel
dfdToStringColorPrimaries
dfdToStringDescriptorType
dfdToStringFlagsBit
dfdToStringSampleDatatypeQualifiersBit
dfdToStringTransferFunction
dfdToStringVendorID
dfdToStringVersionNumber
ktxBUImageFlagsBitString
ktxTexture2_constructCopy
+30
View File
@@ -0,0 +1,30 @@
; Copyright 2019-2020 Mark Callow
; SPDX-License-Identifier: Apache-2.0
; Export internal functions used by internal test programs and the
; validator. Using this file avoids having to decorate those internal
; functions or updating such decorations as the tests evolve. Updating
; this feels less obnoxious.
LIBRARY libktx
EXPORTS
_ZN6basisu9ResamplerD1Ev
_ZN6basisu9ResamplerC1EiiiiNS0_11Boundary_OpEffPKcPNS0_12Contrib_ListES5_ffff
_ZN6basisu9Resampler8put_lineEPKf
_ZN6basisu9Resampler8get_lineEv
_ZN6basisu13image_metrics4calcERKNS_5imageES3_jjbb
_ZN6basisu12compute_ssimERKNS_5imageES2_bb
_ZN6basisu16elemental_vector17increase_capacityEjbjPFvPvS1_jEb
_Z15swizzle_to_rgbaPhS_jyP9swizzle_e
appendLibId
dfdToStringChannelId
dfdToStringColorModel
dfdToStringColorPrimaries
dfdToStringDescriptorType
dfdToStringFlagsBit
dfdToStringSampleDatatypeQualifiersBit
dfdToStringTransferFunction
dfdToStringVendorID
dfdToStringVersionNumber
ktxBUImageFlagsBitString
ktxTexture2_constructCopy
+348
View File
@@ -0,0 +1,348 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/* $Id$ */
/*
* Copyright 2010-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Author: Mark Callow from original code by Georg Kolling
*/
#ifndef KTXINT_H
#define KTXINT_H
#include <math.h>
/* Define this to include the ETC unpack software in the library. */
#ifndef SUPPORT_SOFTWARE_ETC_UNPACK
/* Include for all GL versions because have seen OpenGL ES 3
* implementaions that do not support ETC1 (ARM Mali emulator v1.0)!
*/
#define SUPPORT_SOFTWARE_ETC_UNPACK 1
#endif
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
#define QUOTE(x) #x
#define STR(x) QUOTE(x)
#define KTX2_IDENTIFIER_REF { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }
#define KTX2_HEADER_SIZE (80)
#ifdef __cplusplus
extern "C" {
#endif
/**
* @internal
* @brief used to pass GL context capabilites to subroutines.
*/
#define _KTX_NO_R16_FORMATS 0x0
#define _KTX_R16_FORMATS_NORM 0x1
#define _KTX_R16_FORMATS_SNORM 0x2
#define _KTX_ALL_R16_FORMATS (_KTX_R16_FORMATS_NORM | _KTX_R16_FORMATS_SNORM)
extern GLint _ktxR16Formats;
extern GLboolean _ktxSupportsSRGB;
/**
* @internal
* @~English
* @brief KTX file header.
*
* See the KTX specification for descriptions.
*/
typedef struct KTX_header {
ktx_uint8_t identifier[12];
ktx_uint32_t endianness;
ktx_uint32_t glType;
ktx_uint32_t glTypeSize;
ktx_uint32_t glFormat;
ktx_uint32_t glInternalformat;
ktx_uint32_t glBaseInternalformat;
ktx_uint32_t pixelWidth;
ktx_uint32_t pixelHeight;
ktx_uint32_t pixelDepth;
ktx_uint32_t numberOfArrayElements;
ktx_uint32_t numberOfFaces;
ktx_uint32_t numberOfMipLevels;
ktx_uint32_t bytesOfKeyValueData;
} KTX_header;
/* This will cause compilation to fail if the struct size doesn't match */
typedef int KTX_header_SIZE_ASSERT [sizeof(KTX_header) == KTX_HEADER_SIZE];
/**
* @internal
* @~English
* @brief 32-bit KTX 2 index entry.
*/
typedef struct ktxIndexEntry32 {
ktx_uint32_t byteOffset; /*!< Offset of item from start of file. */
ktx_uint32_t byteLength; /*!< Number of bytes of data in the item. */
} ktxIndexEntry32;
/**
* @internal
* @~English
* @brief 64-bit KTX 2 index entry.
*/
typedef struct ktxIndexEntry64 {
ktx_uint64_t byteOffset; /*!< Offset of item from start of file. */
ktx_uint64_t byteLength; /*!< Number of bytes of data in the item. */
} ktxIndexEntry64;
/**
* @internal
* @~English
* @brief KTX 2 file header.
*
* See the KTX 2 specification for descriptions.
*/
typedef struct KTX_header2 {
ktx_uint8_t identifier[12];
ktx_uint32_t vkFormat;
ktx_uint32_t typeSize;
ktx_uint32_t pixelWidth;
ktx_uint32_t pixelHeight;
ktx_uint32_t pixelDepth;
ktx_uint32_t layerCount;
ktx_uint32_t faceCount;
ktx_uint32_t levelCount;
ktx_uint32_t supercompressionScheme;
ktxIndexEntry32 dataFormatDescriptor;
ktxIndexEntry32 keyValueData;
ktxIndexEntry64 supercompressionGlobalData;
} KTX_header2;
/* This will cause compilation to fail if the struct size doesn't match */
typedef int KTX_header2_SIZE_ASSERT [sizeof(KTX_header2) == KTX2_HEADER_SIZE];
/**
* @internal
* @~English
* @brief KTX 2 level index entry.
*/
typedef struct ktxLevelIndexEntry {
ktx_uint64_t byteOffset; /*!< Offset of level from start of file. */
ktx_uint64_t byteLength;
/*!< Number of bytes of compressed image data in the level. */
ktx_uint64_t uncompressedByteLength;
/*!< Number of bytes of uncompressed image data in the level. */
} ktxLevelIndexEntry;
/**
* @internal
* @~English
* @brief Structure for supplemental information about the texture.
*
* _ktxCheckHeader returns supplemental information about the texture in this
* structure that is derived during checking of the file header.
*/
typedef struct KTX_supplemental_info
{
ktx_uint8_t compressed;
ktx_uint8_t generateMipmaps;
ktx_uint16_t textureDimension;
} KTX_supplemental_info;
/**
* @internal
* @var ktx_uint8_t KTX_supplemental_info::compressed
* @~English
* @brief KTX_TRUE, if this a compressed texture, KTX_FALSE otherwise?
*/
/**
* @internal
* @var ktx_uint8_t KTX_supplemental_info::generateMipmaps
* @~English
* @brief KTX_TRUE, if mipmap generation is required, KTX_FALSE otherwise.
*/
/**
* @internal
* @var ktx_uint16_t KTX_supplemental_info::textureDimension
* @~English
* @brief The number of dimensions, 1, 2 or 3, of data in the texture image.
*/
/*
* @internal
* CheckHeader1
*
* Reads the KTX file header and performs some sanity checking on the values
*/
KTX_error_code ktxCheckHeader1_(KTX_header* pHeader,
KTX_supplemental_info* pSuppInfo);
/*
* @internal
* CheckHeader2
*
* Reads the KTX 2 file header and performs some sanity checking on the values
*/
KTX_error_code ktxCheckHeader2_(KTX_header2* pHeader,
KTX_supplemental_info* pSuppInfo);
/*
* SwapEndian16: Swaps endianness in an array of 16-bit values
*/
void _ktxSwapEndian16(ktx_uint16_t* pData16, ktx_size_t count);
/*
* SwapEndian32: Swaps endianness in an array of 32-bit values
*/
void _ktxSwapEndian32(ktx_uint32_t* pData32, ktx_size_t count);
/*
* SwapEndian32: Swaps endianness in an array of 64-bit values
*/
void _ktxSwapEndian64(ktx_uint64_t* pData64, ktx_size_t count);
/*
* UnpackETC: uncompresses an ETC compressed texture image
*/
KTX_error_code _ktxUnpackETC(const GLubyte* srcETC, const GLenum srcFormat,
ktx_uint32_t active_width, ktx_uint32_t active_height,
GLubyte** dstImage,
GLenum* format, GLenum* internalFormat, GLenum* type,
GLint R16Formats, GLboolean supportsSRGB);
/*
* @internal
* ktxCompressZLIBBounds
*
* Returns upper bound for compresses data using miniz (ZLIB)
*/
ktx_size_t ktxCompressZLIBBounds(ktx_size_t srcLength);
/*
* @internal
* ktxCompressZLIBInt
*
* Compresses data using miniz (ZLIB)
*/
KTX_error_code ktxCompressZLIBInt(unsigned char* pDest,
ktx_size_t* pDestLength,
const unsigned char* pSrc,
ktx_size_t srcLength,
ktx_uint32_t level);
/*
* @internal
* ktxUncompressZLIBInt
*
* Uncompresses data using miniz (ZLIB)
*/
KTX_error_code ktxUncompressZLIBInt(unsigned char* pDest,
ktx_size_t* pDestLength,
const unsigned char* pSrc,
ktx_size_t srcLength);
/*
* Pad nbytes to next multiple of n
*/
#define _KTX_PADN(n, nbytes) (ktx_uint32_t)(n * ceilf((float)(nbytes) / n))
/*
* Calculate bytes of of padding needed to reach next multiple of n.
*/
/* Equivalent to (n * ceil(nbytes / n)) - nbytes */
#define _KTX_PADN_LEN(n, nbytes) \
(ktx_uint32_t)((n * ceilf((float)(nbytes) / n)) - (nbytes))
/*
* Pad nbytes to next multiple of 4
*/
#define _KTX_PAD4(nbytes) _KTX_PADN(4, nbytes)
/*
* Calculate bytes of of padding needed to reach next multiple of 4.
*/
#define _KTX_PAD4_LEN(nbytes) _KTX_PADN_LEN(4, nbytes)
/*
* Pad nbytes to next multiple of 8
*/
#define _KTX_PAD8(nbytes) _KTX_PADN(8, nbytes)
/*
* Calculate bytes of of padding needed to reach next multiple of 8.
*/
#define _KTX_PAD8_LEN(nbytes) _KTX_PADN_LEN(8, nbytes)
/*
* Pad nbytes to KTX_GL_UNPACK_ALIGNMENT
*/
#define _KTX_PAD_UNPACK_ALIGN(nbytes) \
_KTX_PADN(KTX_GL_UNPACK_ALIGNMENT, nbytes)
/*
* Calculate bytes of of padding needed to reach KTX_GL_UNPACK_ALIGNMENT.
*/
#define _KTX_PAD_UNPACK_ALIGN_LEN(nbytes) \
_KTX_PADN_LEN(KTX_GL_UNPACK_ALIGNMENT, nbytes)
/*
======================================
Internal utility functions
======================================
*/
KTX_error_code printKTX2Info2(ktxStream* src, KTX_header2* header);
/*
* fopen a file identified by a UTF-8 path.
*/
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <assert.h>
#include <windows.h>
#include <shellapi.h>
#include <stdlib.h>
// For Windows, we convert the UTF-8 path and mode to UTF-16 path and use
// _wfopen which correctly handles unicode characters.
static inline FILE* ktxFOpenUTF8(char const* path, char const* mode) {
int wpLen = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
int wmLen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
FILE* fp = NULL;
if (wpLen > 0 && wmLen > 0)
{
wchar_t* wpath = (wchar_t*)malloc(wpLen * sizeof(wchar_t));
wchar_t* wmode = (wchar_t*)malloc(wmLen * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, wpLen);
MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, wmLen);
// Returned errno_t value is also set in the global errno.
// Apps use that for error detail as libktx only returns
// KTX_FILE_OPEN_FAILED.
(void)_wfopen_s(&fp, wpath, wmode);
free(wpath);
free(wmode);
return fp;
} else {
assert(KTX_FALSE
&& "ktxFOpenUTF8 called with zero length path or mode.");
return NULL;
}
}
#else
// For other platforms there is no need for any conversion, they
// support UTF-8 natively.
static inline FILE* ktxFOpenUTF8(char const* path, char const* mode) {
return fopen(path, mode);
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* KTXINT_H */
+418
View File
@@ -0,0 +1,418 @@
@mainpage
<!--
Can't put at start. Doxygen requires page title on first line.
Copyright 2019-2020 Mark Callow
SPDX-License-Identifier: Apache-2.0
-->
libktx is a small library of functions for creating and reading KTX (Khronos
TeXture) files, version 1 and 2 and instantiating OpenGL&reg; and OpenGL&reg; 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
<a href="https://registry.khronos.org/KTX/specs/2.0/ktxspec.v2.html">
formal specification.</a>
@authors
<a href="http://github.com/MarkCallow">Mark Callow</a>,
formerly at <a href="http://www.hicorp.co.jp">HI Corporation</a>\n
Mátyás Császár and Daniel Rákos, <a href="https://www.rastergrid.com/">RasterGrid</a>\n
Wasim Abbas, <a href="https://www.arm.com/">Arm</a>\n
Andreas Atteneder, Independent\n
Georg Kolling, <a href="http://www.imgtec.com">Imagination Technology</a>\n
Jacob Str&ouml;m, <a href="http://www.ericsson.com">Ericsson AB</a>
@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 <ktx.h>
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 <ktx.h>
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 <vulkan/vulkan.h>
#include <ktxvulkan.h>
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 <ktx.h>
#include <vkformat_enum.h>
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 <ktx.h>
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 <ktx.h>
#include <vkformat_enum.h>
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, &params);
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 <ktx.h>
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 <ktx.h>
#include <vkformat_enum.h>
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, &params);
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);
~~~~~~~~~~~~~~~~
+561
View File
@@ -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_size_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_size_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 < 0 || (ktx_size_t)pos > 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;
}
+43
View File
@@ -0,0 +1,43 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2010-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Interface of ktxStream for memory.
*
* @author Maksim Kolesin
* @author Georg Kolling, Imagination Technology
* @author Mark Callow, HI Corporation
*/
#ifndef MEMSTREAM_H
#define MEMSTREAM_H
#include "ktx.h"
/*
* Initialize a ktxStream to a ktxMemStream with internally
* allocated memory. Can be read or written.
*/
KTX_error_code ktxMemStream_construct(ktxStream* str,
ktx_bool_t freeOnDestruct);
/*
* Initialize a ktxStream to a read-only ktxMemStream reading
* from an array of bytes.
*/
KTX_error_code ktxMemStream_construct_ro(ktxStream* str,
const ktx_uint8_t* pBytes,
const ktx_size_t size);
void ktxMemStream_destruct(ktxStream* str);
KTX_error_code ktxMemStream_getdata(ktxStream* str, ktx_uint8_t** ppBytes);
#endif /* MEMSTREAM_H */
+149
View File
@@ -0,0 +1,149 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2023-2023 The Khronos Group Inc.
* Copyright 2023-2023 RasterGrid Kft.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Wrapper functions for ZLIB compression/decompression using miniz.
*
* @author Daniel Rakos, RasterGrid
*/
#include "ktx.h"
#include "ktxint.h"
#include <assert.h>
#if !KTX_FEATURE_WRITE
// The reader does not link with the basisu components that already include a
// definition of miniz so we include it here explicitly.
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wextra"
#pragma GCC diagnostic ignored "-Wmisleading-indentation"
#endif
#include "basisu/encoder/basisu_miniz.h"
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#else
// Otherwise we only declare the interfaces and link with the basisu version.
// This is needed because while miniz is defined as a header in basisu it's
// not declaring the functions as static or inline, hence causing multiple
// conflicting definitions at link-time.
namespace buminiz {
typedef unsigned long mz_ulong;
enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };
mz_ulong mz_compressBound(mz_ulong source_len);
int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
}
#endif
using namespace buminiz;
extern "C" {
/**
* @internal
* @~English
* @brief Returns upper bound for compresses data using miniz (ZLIB).
*
* @param srcLength source data length
*
* @author Daniel Rakos, RasterGrid
*/
ktx_size_t ktxCompressZLIBBounds(ktx_size_t srcLength) {
return mz_compressBound((mz_ulong)srcLength);
}
/**
* @internal
* @~English
* @brief Compresses data using miniz (ZLIB)
*
* @param pDest destination data buffer
* @param pDestLength destination data buffer size
* (filled with written byte count on success)
* @param pSrc source data buffer
* @param srcLength source data size
* @param level compression level (between 1 and 9)
*
* @author Daniel Rakos, RasterGrid
*/
KTX_error_code ktxCompressZLIBInt(unsigned char* pDest,
ktx_size_t* pDestLength,
const unsigned char* pSrc,
ktx_size_t srcLength,
ktx_uint32_t level) {
if ((srcLength | *pDestLength) > 0xFFFFFFFFU) return KTX_INVALID_VALUE;
mz_ulong mzCompressedSize = (mz_ulong)*pDestLength;
int status = mz_compress2(pDest, &mzCompressedSize, pSrc, (mz_ulong)srcLength, level);
switch (status) {
case MZ_OK:
*pDestLength = mzCompressedSize;
return KTX_SUCCESS;
case MZ_PARAM_ERROR:
return KTX_INVALID_VALUE;
case MZ_BUF_ERROR:
#ifdef DEBUG
assert(false && "Deflate dstSize too small.");
#endif
return KTX_OUT_OF_MEMORY;
case MZ_MEM_ERROR:
#ifdef DEBUG
assert(false && "Deflate workspace too small.");
#endif
return KTX_OUT_OF_MEMORY;
default:
// The remaining errors look like they should only
// occur during decompression but just in case.
#ifdef DEBUG
assert(true);
#endif
return KTX_INVALID_OPERATION;
}
}
/**
* @internal
* @~English
* @brief Uncompresses data using miniz (ZLIB)
*
* @param pDest destination data buffer
* @param pDestLength destination data buffer size
* (filled with written byte count on success)
* @param pSrc source data buffer
* @param srcLength source data size
*
* @author Daniel Rakos, RasterGrid
*/
KTX_error_code ktxUncompressZLIBInt(unsigned char* pDest,
ktx_size_t* pDestLength,
const unsigned char* pSrc,
ktx_size_t srcLength) {
if ((srcLength | *pDestLength) > 0xFFFFFFFFU) return KTX_INVALID_VALUE;
mz_ulong mzUncompressedSize = (mz_ulong)*pDestLength;
int status = mz_uncompress(pDest, &mzUncompressedSize, pSrc, (mz_ulong)srcLength);
switch (status) {
case MZ_OK:
*pDestLength = mzUncompressedSize;
return KTX_SUCCESS;
case MZ_BUF_ERROR:
return KTX_DECOMPRESS_LENGTH_ERROR; // buffer too small
case MZ_MEM_ERROR:
return KTX_OUT_OF_MEMORY;
default:
return KTX_FILE_DATA_ERROR;
}
}
}
+1164
View File
File diff suppressed because it is too large Load Diff
+154
View File
@@ -0,0 +1,154 @@
/* -*- 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 Functions to return a string corresponding to various enumerations.
*
* @author Mark Callow, HI Corporation
*/
#include "ktx.h"
#include "basis_sgd.h"
static const char* const errorStrings[] = {
"Operation succeeded.", /* KTX_SUCCESS */
"File data is inconsistent with KTX spec.", /* KTX_FILE_DATA_ERROR */
"File is a pipe; seek operations not possible.", /* KTX_FILE_ISPIPE */
"File open failed.", /* KTX_FILE_OPEN_FAILED */
"Operation would exceed the max file size.", /* KTX_FILE_OVERFLOW */
"File read error.", /* KTX_FILE_READ_ERROR */
"File seek error.", /* KTX_FILE_SEEK_ERROR */
"File does not have enough data for request.", /* KTX_FILE_UNEXPECTED_EOF */
"File write error.", /* KTX_FILE_WRITE_ERROR */
"GL error occurred.", /* KTX_GL_ERROR */
"Operation not allowed in the current state.", /* KTX_INVALID_OPERATION */
"Invalid parameter value.", /* KTX_INVALID_VALUE */
"Metadata key or loader-required GPU function not found.", /* KTX_NOT_FOUND */
"Out of memory.", /* KTX_OUT_OF_MEMORY */
"Transcoding of block compressed texture failed.",/* KTX_TRANSCODE_FAILED */
"Not a KTX file.", /* KTX_UNKNOWN_FILE_FORMAT */
"Texture type not supported.", /* KTX_UNSUPPORTED_TEXTURE_TYPE */
"Feature not included in in-use library or not yet implemented.", /* KTX_UNSUPPORTED_FEATURE */
"Library dependency (OpenGL or Vulkan) not linked into application.", /* KTX_LIBRARY_NOT_LINKED */
"Decompressed byte count does not match expected byte size", /* KTX_DECOMPRESS_LENGTH_ERROR */
"Checksum mismatch when decompressing" /* KTX_DECOMPRESS_CHECKSUM_ERROR */
};
/* This will cause compilation to fail if number of messages and codes doesn't match */
typedef int errorStrings_SIZE_ASSERT[sizeof(errorStrings) / sizeof(char*) - 1 == KTX_ERROR_MAX_ENUM];
/**
* @~English
* @brief Return a string corresponding to a KTX error code.
*
* @param error the error code for which to return a string
*
* @return pointer to the message string.
*
* @internal Use UTF-8 for translated message strings.
*
* @author Mark Callow
*/
const char* ktxErrorString(KTX_error_code error)
{
if (error < 0 || error > KTX_ERROR_MAX_ENUM)
return "Unrecognized error code";
return errorStrings[error];
}
/**
* @~English
* @brief Return a string corresponding to a transcode format enumeration.
*
* @param format the transcode format for which to return a string.
*
* @return pointer to the message string.
*
* @internal Use UTF-8 for translated message strings.
*
* @author Mark Callow
*/
const char* ktxTranscodeFormatString(ktx_transcode_fmt_e format)
{
switch (format) {
case KTX_TTF_ETC1_RGB: return "ETC1_RGB";
case KTX_TTF_ETC2_RGBA: return "ETC2_RGBA";
case KTX_TTF_BC1_RGB: return "BC1_RGB";
case KTX_TTF_BC3_RGBA: return "BC3_RGBA";
case KTX_TTF_BC4_R: return "BC4_R";
case KTX_TTF_BC5_RG: return "BC5_RG";
case KTX_TTF_BC7_RGBA: return "BC7_RGBA";
case KTX_TTF_PVRTC1_4_RGB: return "PVRTC1_4_RGB";
case KTX_TTF_PVRTC1_4_RGBA: return "PVRTC1_4_RGBA";
case KTX_TTF_ASTC_4x4_RGBA: return "ASTC_4x4_RGBA";
case KTX_TTF_RGBA32: return "RGBA32";
case KTX_TTF_RGB565: return "RGB565";
case KTX_TTF_BGR565: return "BGR565";
case KTX_TTF_RGBA4444: return "RGBA4444";
case KTX_TTF_PVRTC2_4_RGB: return "PVRTC2_4_RGB";
case KTX_TTF_PVRTC2_4_RGBA: return "PVRTC2_4_RGBA";
case KTX_TTF_ETC2_EAC_R11: return "ETC2_EAC_R11";
case KTX_TTF_ETC2_EAC_RG11: return "ETC2_EAC_RG11";
case KTX_TTF_ETC: return "ETC";
case KTX_TTF_BC1_OR_3: return "BC1 or BC3";
default: return "Unrecognized format";
}
}
/**
* @~English
* @brief Return a string corresponding to a supercompressionScheme enumeration.
*
* @param scheme the supercompression scheme for which to return a string.
*
* @return pointer to the message string.
*
* @internal Use UTF-8 for translated message strings.
*
* @author Mark Callow
*/
const char *
ktxSupercompressionSchemeString(ktxSupercmpScheme scheme)
{
switch (scheme) {
case KTX_SS_NONE: return "KTX_SS_NONE";
case KTX_SS_BASIS_LZ: return "KTX_SS_BASIS_LZ";
case KTX_SS_ZSTD: return "KTX_SS_ZSTD";
case KTX_SS_ZLIB: return "KTX_SS_ZLIB";
default:
if (scheme < KTX_SS_BEGIN_VENDOR_RANGE
|| scheme >= KTX_SS_BEGIN_RESERVED)
return "Invalid scheme value";
else
return "Vendor or reserved scheme";
}
}
/**
* @~English
* @brief Return a string corresponding to a bu_image_flags bit.
*
* @param bit_index the bu_image_flag bit to test.
* @param bit_value the bu_image_flag bit value.
*
* @return pointer to the message string or NULL otherwise.
*
* @internal Use UTF-8 for translated message strings.
*/
const char* ktxBUImageFlagsBitString(ktx_uint32_t bit_index, bool bit_value)
{
if (!bit_value)
return NULL;
switch (1u << bit_index) {
case ETC1S_P_FRAME: return "ETC1S_P_FRAME";
default: return NULL;
}
}
+57
View File
@@ -0,0 +1,57 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/* $Id: 02ea6de2d8db512ca3af08f48b98ab5f6c35e7e5 $ */
/*
* Copyright 2010-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
#include <KHR/khrplatform.h>
#include "ktx.h"
/*
* SwapEndian16: Swaps endianness in an array of 16-bit values
*/
void
_ktxSwapEndian16(khronos_uint16_t* pData16, ktx_size_t count)
{
for (ktx_size_t i = 0; i < count; ++i)
{
khronos_uint16_t x = *pData16;
*pData16++ = (x << 8) | (x >> 8);
}
}
/*
* SwapEndian32: Swaps endianness in an array of 32-bit values
*/
void
_ktxSwapEndian32(khronos_uint32_t* pData32, ktx_size_t count)
{
for (ktx_size_t i = 0; i < count; ++i)
{
khronos_uint32_t x = *pData32;
*pData32++ = (x << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | (x >> 24);
}
}
/*
* SwapEndian364: Swaps endianness in an array of 32-bit values
*/
void
_ktxSwapEndian64(khronos_uint64_t* pData64, ktx_size_t count)
{
for (ktx_size_t i = 0; i < count; ++i)
{
khronos_uint64_t x = *pData64;
*pData64++ = (x << 56) | ((x & 0xFF00) << 40) | ((x & 0xFF0000) << 24)
| ((x & 0xFF000000) << 8 ) | ((x & 0xFF00000000) >> 8)
| ((x & 0xFF0000000000) >> 24)
| ((x & 0xFF000000000000) << 40) | (x >> 56);
}
}
+882
View File
@@ -0,0 +1,882 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief ktxTexture implementation.
*
* @author Mark Callow, github.com/MarkCallow
*/
#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS
#ifndef __cplusplus
#undef inline
#define inline __inline
#endif // __cplusplus
#endif
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "ktx.h"
#include "ktxint.h"
#include "formatsize.h"
#include "filestream.h"
#include "memstream.h"
#include "texture1.h"
#include "texture2.h"
#include "unused.h"
ktx_size_t ktxTexture_GetDataSize(ktxTexture* This);
static ktx_uint32_t padRow(ktx_uint32_t* rowBytes);
/**
* @memberof ktxTexture @private
* @~English
* @brief Construct (initialize) a ktxTexture base class instance.
*
* @param[in] This pointer to a ktxTexture-sized block of memory to
* initialize.
* @param[in] createInfo pointer to a ktxTextureCreateInfo struct with
* information describing the texture.
* @param[in] formatSize pointer to a ktxFormatSize giving size information
* about the texture's elements.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a
* valid OpenGL internal format value.
* @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2
* or 3.
* @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in
* @p createInfo is 0.
* @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.
* @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.
* @exception KTX_INVALID_OPERATION
* The <tt>base{Width,Height,Depth}</tt> specified
* in @p createInfo are inconsistent with
* @c numDimensions.
* @exception KTX_INVALID_OPERATION
* @p createInfo is requesting a 3D array or
* 3D cubemap texture.
* @exception KTX_INVALID_OPERATION
* @p createInfo is requesting a cubemap with
* non-square or non-2D images.
* @exception KTX_INVALID_OPERATION
* @p createInfo is requesting more mip levels
* than needed for the specified
* <tt>base{Width,Height,Depth}</tt>.
* @exception KTX_OUT_OF_MEMORY Not enough memory for the texture.
*/
KTX_error_code
ktxTexture_construct(ktxTexture* This,
const ktxTextureCreateInfo* const createInfo,
ktxFormatSize* formatSize)
{
DECLARE_PROTECTED(ktxTexture);
memset(This, 0, sizeof(*This));
This->_protected = (struct ktxTexture_protected*)malloc(sizeof(*prtctd));
if (!This->_protected)
return KTX_OUT_OF_MEMORY;
prtctd = This->_protected;
memset(prtctd, 0, sizeof(*prtctd));
memcpy(&prtctd->_formatSize, formatSize, sizeof(prtctd->_formatSize));
This->isCompressed = (formatSize->flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);
This->orientation.x = KTX_ORIENT_X_RIGHT;
This->orientation.y = KTX_ORIENT_Y_DOWN;
This->orientation.z = KTX_ORIENT_Z_OUT;
/* Check texture dimensions. KTX files can store 8 types of textures:
* 1D, 2D, 3D, cube, and array variants of these.
*/
if (createInfo->numDimensions < 1 || createInfo->numDimensions > 3)
return KTX_INVALID_VALUE;
if (createInfo->baseWidth == 0 || createInfo->baseHeight == 0
|| createInfo->baseDepth == 0)
return KTX_INVALID_VALUE;
switch (createInfo->numDimensions) {
case 1:
if (createInfo->baseHeight > 1 || createInfo->baseDepth > 1)
return KTX_INVALID_OPERATION;
break;
case 2:
if (createInfo->baseDepth > 1)
return KTX_INVALID_OPERATION;
break;
case 3:
/* 3D array textures and 3D cubemaps are not supported by either
* OpenGL or Vulkan.
*/
if (createInfo->isArray || createInfo->numFaces != 1
|| createInfo->numLayers != 1)
return KTX_INVALID_OPERATION;
break;
}
This->numDimensions = createInfo->numDimensions;
This->baseWidth = createInfo->baseWidth;
This->baseDepth = createInfo->baseDepth;
This->baseHeight = createInfo->baseHeight;
if (createInfo->numLayers == 0)
return KTX_INVALID_VALUE;
This->numLayers = createInfo->numLayers;
This->isArray = createInfo->isArray;
if (createInfo->numFaces == 6) {
if (This->numDimensions != 2) {
/* cube map needs 2D faces */
return KTX_INVALID_OPERATION;
}
if (createInfo->baseWidth != createInfo->baseHeight) {
/* cube maps require square images */
return KTX_INVALID_OPERATION;
}
This->isCubemap = KTX_TRUE;
} else if (createInfo->numFaces != 1) {
/* numFaces must be either 1 or 6 */
return KTX_INVALID_VALUE;
}
This->numFaces = createInfo->numFaces;
/* Check number of mipmap levels */
if (createInfo->numLevels == 0)
return KTX_INVALID_VALUE;
This->numLevels = createInfo->numLevels;
This->generateMipmaps = createInfo->generateMipmaps;
if (createInfo->numLevels > 1) {
GLuint max_dim = MAX(MAX(createInfo->baseWidth, createInfo->baseHeight),
createInfo->baseDepth);
if (max_dim < ((GLuint)1 << (This->numLevels - 1)))
{
/* Can't have more mip levels than 1 + log2(max(width, height, depth)) */
return KTX_INVALID_OPERATION;
}
}
ktxHashList_Construct(&This->kvDataHead);
return KTX_SUCCESS;
}
/**
* @memberof ktxTexture @private
* @~English
* @brief Construct (initialize) the part of a ktxTexture base class that is
* not related to the stream contents.
*
* @param[in] This pointer to a ktxTexture-sized block of memory to
* initialize.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*/
KTX_error_code
ktxTexture_constructFromStream(ktxTexture* This, ktxStream* pStream,
ktxTextureCreateFlags createFlags)
{
ktxStream* stream;
UNUSED(createFlags); // Reference to keep compiler happy.
assert(This != NULL);
assert(pStream->data.mem != NULL);
assert(pStream->type == eStreamTypeFile
|| pStream->type == eStreamTypeMemory
|| pStream->type == eStreamTypeCustom);
This->_protected = (struct ktxTexture_protected *)
malloc(sizeof(struct ktxTexture_protected));
stream = ktxTexture_getStream(This);
// Copy stream info into struct for later use.
*stream = *pStream;
This->orientation.x = KTX_ORIENT_X_RIGHT;
This->orientation.y = KTX_ORIENT_Y_DOWN;
This->orientation.z = KTX_ORIENT_Z_OUT;
return KTX_SUCCESS;
}
/**
* @memberof ktxTexture @private
* @~English
* @brief Free the memory associated with the texture contents
*
* @param[in] This pointer to the ktxTextureInt whose texture contents are
* to be freed.
*/
void
ktxTexture_destruct(ktxTexture* This)
{
ktxStream stream = *(ktxTexture_getStream(This));
if (stream.data.file != NULL)
stream.destruct(&stream);
if (This->kvDataHead != NULL)
ktxHashList_Destruct(&This->kvDataHead);
if (This->kvData != NULL)
free(This->kvData);
if (This->pData != NULL)
free(This->pData);
free(This->_protected);
}
/**
* @defgroup reader Reader
* @brief Read KTX-formatted data.
* @{
*/
typedef enum { KTX1, KTX2 } ktxFileType_;
typedef union {
KTX_header ktx;
KTX_header2 ktx2;
} ktxHeaderUnion_;
/**
* @memberof ktxTexture @private
* @~English
* @brief Determine if stream data is KTX1 or KTX2.
*
* @param pStream pointer to the ktxStream to examine.
* @param pFileType pointer to a ktxFileType enum where the type of the data
* will be written.
* @param pHeader pointer to a ktxHeaderUnion where the header info. will be
* written.
*/
static KTX_error_code
ktxDetermineFileType_(ktxStream* pStream, ktxFileType_* pFileType,
ktxHeaderUnion_* pHeader)
{
ktx_uint8_t ktx_ident_ref[12] = KTX_IDENTIFIER_REF;
ktx_uint8_t ktx2_ident_ref[12] = KTX2_IDENTIFIER_REF;
KTX_error_code result;
assert(pStream != NULL && pFileType != NULL);
assert(pStream->data.mem != NULL);
assert(pStream->type == eStreamTypeFile
|| pStream->type == eStreamTypeMemory
|| pStream->type == eStreamTypeCustom);
result = pStream->read(pStream, pHeader, sizeof(ktx2_ident_ref));
if (result == KTX_SUCCESS) {
#if BIG_ENDIAN
// byte swap the heaader fields
#endif
// Compare identifier, is this a KTX or KTX2 file?
if (!memcmp(pHeader->ktx.identifier, ktx_ident_ref, 12)) {
*pFileType = KTX1;
} else if (!memcmp(pHeader->ktx2.identifier, ktx2_ident_ref, 12)) {
*pFileType = KTX2;
} else {
return KTX_UNKNOWN_FILE_FORMAT;
}
// Read rest of header.
if (*pFileType == KTX1) {
// Read rest of header.
result = pStream->read(pStream, &pHeader->ktx.endianness,
KTX_HEADER_SIZE - sizeof(ktx_ident_ref));
} else {
result = pStream->read(pStream, &pHeader->ktx2.vkFormat,
KTX2_HEADER_SIZE - sizeof(ktx2_ident_ref));
}
}
return result;
}
/**
* @memberof ktxTexture
* @~English
* @brief Create a ktx1 or ktx2 texture according to the stream
* data.
*
* See @ref ktxTexture1::ktxTexture1_CreateFromStream
* "ktxTexture1_CreateFromStream" or
* @ref ktxTexture2::ktxTexture2_CreateFromStream
* "ktxTexture2_CreateFromStream" for details.
*/
KTX_error_code
ktxTexture_CreateFromStream(ktxStream* pStream,
ktxTextureCreateFlags createFlags,
ktxTexture** newTex)
{
ktxHeaderUnion_ header;
ktxFileType_ fileType;
KTX_error_code result;
ktxTexture* tex;
result = ktxDetermineFileType_(pStream, &fileType, &header);
if (result != KTX_SUCCESS)
return result;
if (fileType == KTX1) {
ktxTexture1* tex1 = (ktxTexture1*)malloc(sizeof(ktxTexture1));
if (tex1 == NULL)
return KTX_OUT_OF_MEMORY;
memset(tex1, 0, sizeof(ktxTexture1));
result = ktxTexture1_constructFromStreamAndHeader(tex1, pStream,
&header.ktx,
createFlags);
tex = ktxTexture(tex1);
} else {
ktxTexture2* tex2 = (ktxTexture2*)malloc(sizeof(ktxTexture2));
if (tex2 == NULL)
return KTX_OUT_OF_MEMORY;
memset(tex2, 0, sizeof(ktxTexture2));
result = ktxTexture2_constructFromStreamAndHeader(tex2, pStream,
&header.ktx2,
createFlags);
tex = ktxTexture(tex2);
}
if (result == KTX_SUCCESS)
*newTex = (ktxTexture*)tex;
else {
free(tex);
*newTex = NULL;
}
return result;
}
/**
* @memberof ktxTexture
* @~English
* @brief Create a ktxTexture1 or ktxTexture2 from a stdio stream according
* to the stream data.
*
* See @ref ktxTexture1::ktxTexture1_CreateFromStdioStream
* "ktxTexture1_CreateFromStdioStream" or
* @ref ktxTexture2::ktxTexture2_CreateFromStdioStream
* "ktxTexture2_CreateFromStdioStream" for details.
*/
KTX_error_code
ktxTexture_CreateFromStdioStream(FILE* stdioStream,
ktxTextureCreateFlags createFlags,
ktxTexture** newTex)
{
ktxStream stream;
KTX_error_code result;
if (stdioStream == NULL || newTex == NULL)
return KTX_INVALID_VALUE;
result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);
if (result == KTX_SUCCESS) {
result = ktxTexture_CreateFromStream(&stream, createFlags, newTex);
}
return result;
}
/**
* @memberof ktxTexture
* @~English
* @brief Create a ktxTexture1 or ktxTexture2 from a named KTX file according
* to the file contents.
*
* See @ref ktxTexture1::ktxTexture1_CreateFromNamedFile
* "ktxTexture1_CreateFromNamedFile" or
* @ref ktxTexture2::ktxTexture2_CreateFromNamedFile
* "ktxTexture2_CreateFromNamedFile" for details.
*/
KTX_error_code
ktxTexture_CreateFromNamedFile(const char* const filename,
ktxTextureCreateFlags createFlags,
ktxTexture** newTex)
{
KTX_error_code result;
ktxStream stream;
FILE* file;
if (filename == NULL || newTex == NULL)
return KTX_INVALID_VALUE;
file = ktxFOpenUTF8(filename, "rb");
if (!file)
return KTX_FILE_OPEN_FAILED;
result = ktxFileStream_construct(&stream, file, KTX_TRUE);
if (result == KTX_SUCCESS) {
result = ktxTexture_CreateFromStream(&stream, createFlags, newTex);
}
return result;
}
/**
* @memberof ktxTexture
* @~English
* @brief Create a ktxTexture1 or ktxTexture2 from KTX-formatted data in memory
* according to the data contents.
*
* See @ref ktxTexture1::ktxTexture1_CreateFromMemory
* "ktxTexture1_CreateFromMemory" or
* @ref ktxTexture2::ktxTexture2_CreateFromMemory
* "ktxTexture2_CreateFromMemory" for details.
*/
KTX_error_code
ktxTexture_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
ktxTextureCreateFlags createFlags,
ktxTexture** newTex)
{
KTX_error_code result;
ktxStream stream;
if (bytes == NULL || newTex == NULL || size == 0)
return KTX_INVALID_VALUE;
result = ktxMemStream_construct_ro(&stream, bytes, size);
if (result == KTX_SUCCESS) {
result = ktxTexture_CreateFromStream(&stream, createFlags, newTex);
}
return result;}
/**
* @memberof ktxTexture
* @~English
* @brief Return a pointer to the texture image data.
*
* @param[in] This pointer to the ktxTexture object of interest.
*/
ktx_uint8_t*
ktxTexture_GetData(ktxTexture* This)
{
return This->pData;
}
/**
* @memberof ktxTexture
* @~English
* @brief Return the total size of the texture image data in bytes.
*
* For a ktxTexture2 with supercompressionScheme != KTX_SS_NONE this will
* return the deflated size of the data.
*
* @param[in] This pointer to the ktxTexture object of interest.
*/
ktx_size_t
ktxTexture_GetDataSize(ktxTexture* This)
{
assert(This != NULL);
return This->dataSize;
}
/**
* @memberof ktxTexture
* @~English
* @brief Return the size in bytes of an elements of a texture's
* images.
*
* For uncompressed textures an element is one texel. For compressed
* textures it is one block.
*
* @param[in] This pointer to the ktxTexture object of interest.
*/
ktx_uint32_t
ktxTexture_GetElementSize(ktxTexture* This)
{
assert (This != NULL);
return (This->_protected->_formatSize.blockSizeInBits / 8);
}
/**
* @memberof ktxTexture @private
* @~English
* @brief Calculate & return the size in bytes of an image at the specified
* mip level.
*
* For arrays, this is the size of layer, for cubemaps, the size of a face
* and for 3D textures, the size of a depth slice.
*
* The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.
*
* @param[in] This pointer to the ktxTexture object of interest.
* @param[in] level level of interest.
* @param[in] fv enum specifying format version for which to calculate
* image size.
*/
ktx_size_t
ktxTexture_calcImageSize(ktxTexture* This, ktx_uint32_t level,
ktxFormatVersionEnum fv)
{
DECLARE_PROTECTED(ktxTexture);
struct blockCount {
ktx_uint32_t x, y;
} blockCount;
ktx_uint32_t blockSizeInBytes;
ktx_uint32_t rowBytes;
assert (This != NULL);
float levelWidth = (float)(This->baseWidth >> level);
float levelHeight = (float)(This->baseHeight >> level);
// Round up to next whole block. We can't use KTX_PADN because some of
// the block sizes are not powers of 2.
blockCount.x
= (ktx_uint32_t)ceilf(levelWidth / prtctd->_formatSize.blockWidth);
blockCount.y
= (ktx_uint32_t)ceilf(levelHeight / prtctd->_formatSize.blockHeight);
blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x);
blockCount.y = MAX(prtctd->_formatSize.minBlocksY, blockCount.y);
blockSizeInBytes = prtctd->_formatSize.blockSizeInBits / 8;
if (prtctd->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT) {
assert(This->isCompressed);
return blockCount.x * blockCount.y * blockSizeInBytes;
} else {
assert(prtctd->_formatSize.blockWidth == 1U
&& prtctd->_formatSize.blockHeight == 1U
&& prtctd->_formatSize.blockDepth == 1U);
rowBytes = blockCount.x * blockSizeInBytes;
if (fv == KTX_FORMAT_VERSION_ONE)
(void)padRow(&rowBytes);
return rowBytes * blockCount.y;
}
}
/**
* @memberof ktxTexture
* @~English
* @brief Iterate over the levels or faces in a ktxTexture object.
*
* Blocks of image data are passed to an application-supplied callback
* function. This is not a strict per-image iteration. Rather it reflects how
* OpenGL needs the images. For most textures the block of data includes all
* images of a mip level which implies all layers of an array. However, for
* non-array cube map textures the block is a single face of the mip level,
* i.e the callback is called once for each face.
*
* This function works even if @p This->pData == 0 so it can be used to
* obtain offsets and sizes for each level by callers who have loaded the data
* externally.
*
* @param[in] This pointer to the ktxTexture object of interest.
* @param[in,out] iterCb the address of a callback function which is called
* with the data for each image block.
* @param[in,out] userdata the address of application-specific data which is
* passed to the callback along with the image data.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error. The
* following are returned directly by this function. @p iterCb may
* return these for other causes or may return additional errors.
*
* @exception KTX_FILE_DATA_ERROR Mip level sizes are increasing not
* decreasing
* @exception KTX_INVALID_VALUE @p This is @c NULL or @p iterCb is @c NULL.
*
*/
KTX_error_code
ktxTexture_IterateLevelFaces(ktxTexture* This, PFNKTXITERCB iterCb,
void* userdata)
{
ktx_uint32_t miplevel;
KTX_error_code result = KTX_SUCCESS;
if (This == NULL)
return KTX_INVALID_VALUE;
if (iterCb == NULL)
return KTX_INVALID_VALUE;
for (miplevel = 0; miplevel < This->numLevels; ++miplevel)
{
ktx_uint32_t faceLodSize;
ktx_uint32_t face;
ktx_uint32_t innerIterations;
GLsizei width, height, depth;
/* Array textures have the same number of layers at each mip level. */
width = MAX(1, This->baseWidth >> miplevel);
height = MAX(1, This->baseHeight >> miplevel);
depth = MAX(1, This->baseDepth >> miplevel);
faceLodSize = (ktx_uint32_t)ktxTexture_calcFaceLodSize(
This, miplevel);
/* All array layers are passed in a group because that is how
* GL & Vulkan need them. Hence no
* for (layer = 0; layer < This->numLayers)
*/
if (This->isCubemap && !This->isArray)
innerIterations = This->numFaces;
else
innerIterations = 1;
for (face = 0; face < innerIterations; ++face)
{
/* And all z_slices are also passed as a group hence no
* for (slice = 0; slice < This->depth)
*/
ktx_size_t offset;
ktxTexture_GetImageOffset(This, miplevel, 0, face, &offset);
result = iterCb(miplevel, face,
width, height, depth,
faceLodSize, This->pData + offset, userdata);
if (result != KTX_SUCCESS)
break;
}
}
return result;
}
/**
* @internal
* @brief Calculate and apply the padding needed to comply with
* KTX_GL_UNPACK_ALIGNMENT.
*
* For uncompressed textures, KTX format specifies KTX_GL_UNPACK_ALIGNMENT = 4.
*
* @param[in,out] rowBytes pointer to variable containing the packed no. of
* bytes in a row. The no. of bytes after padding
* is written into this location.
* @return the no. of bytes of padding.
*/
static ktx_uint32_t
padRow(ktx_uint32_t* rowBytes)
{
ktx_uint32_t rowPadding;
assert (rowBytes != NULL);
rowPadding = _KTX_PAD_UNPACK_ALIGN_LEN(*rowBytes);
*rowBytes += rowPadding;
return rowPadding;
}
/**
* @memberof ktxTexture @private
* @~English
* @brief Calculate the size of an array layer at the specified mip level.
*
* The size of a layer is the size of an image * either the number of faces
* or the number of depth slices. This is the size of a layer as needed to
* find the offset within the array of images of a level and layer so the size
* reflects any @c cubePadding.
*
* @param[in] This pointer to the ktxTexture object of interest.
* @param[in] level level whose layer size to return.
*
* @return the layer size in bytes.
*/
ktx_size_t
ktxTexture_layerSize(ktxTexture* This, ktx_uint32_t level,
ktxFormatVersionEnum fv)
{
/*
* As there are no 3D cubemaps, the image's z block count will always be
* 1 for cubemaps and numFaces will always be 1 for 3D textures so the
* multiply is safe. 3D cubemaps, if they existed, would require
* imageSize * (blockCount.z + This->numFaces);
*/
DECLARE_PROTECTED(ktxTexture);
ktx_uint32_t blockCountZ;
ktx_size_t imageSize, layerSize;
assert (This != NULL);
assert (prtctd->_formatSize.blockDepth != 0);
blockCountZ = ((This->baseDepth >> level) + prtctd->_formatSize.blockDepth - 1) / prtctd->_formatSize.blockDepth;
blockCountZ = MAX(1, blockCountZ);
imageSize = ktxTexture_calcImageSize(This, level, fv);
layerSize = imageSize * blockCountZ;
if (fv == KTX_FORMAT_VERSION_ONE && KTX_GL_UNPACK_ALIGNMENT != 4) {
if (This->isCubemap && !This->isArray) {
/* cubePadding. NOTE: this adds padding after the last face too. */
layerSize += _KTX_PAD4(layerSize);
}
}
return layerSize * This->numFaces;
}
/**
* @memberof ktxTexture @private
* @~English
* @brief Calculate the size of the specified mip level.
*
* The size of a level is the size of a layer * the number of layers.
*
* @param[in] This pointer to the ktxTexture object of interest.
* @param[in] level level whose layer size to return.
*
* @return the level size in bytes.
*/
ktx_size_t
ktxTexture_calcLevelSize(ktxTexture* This, ktx_uint32_t level,
ktxFormatVersionEnum fv)
{
assert (This != NULL);
assert (level < This->numLevels);
return ktxTexture_layerSize(This, level, fv) * This->numLayers;
}
/**
* @memberof ktxTexture @private
* @~English
* @brief Calculate the faceLodSize of the specified mip level.
*
* The faceLodSize of a level for most textures is the size of a level. For
* non-array cube map textures is the size of a face. This is the size that
* must be provided to OpenGL when uploading textures. Faces get uploaded 1
* at a time while all layers of an array or all slices of a 3D texture are
* uploaded together.
*
* @param[in] This pointer to the ktxTexture object of interest.
* @param[in] level level whose layer size to return.
*
* @return the faceLodSize size in bytes.
*/
ktx_size_t
ktxTexture_doCalcFaceLodSize(ktxTexture* This, ktx_uint32_t level,
ktxFormatVersionEnum fv)
{
/*
* For non-array cubemaps this is the size of a face. For everything
* else it is the size of the level.
*/
if (This->isCubemap && !This->isArray)
return ktxTexture_calcImageSize(This, level, fv);
else
return ktxTexture_calcLevelSize(This, level, fv);
}
/**
* @memberof ktxTexture @private
* @~English
* @brief Return the number of bytes needed to store all the image data for
* a ktxTexture.
*
* The caclulated size does not include space for storing the @c imageSize
* fields of each mip level.
*
* @param[in] This pointer to the ktxTexture object of interest.
* @param[in] fv enum specifying format version for which to calculate
* image size.
*
* @return the data size in bytes.
*/
ktx_size_t
ktxTexture_calcDataSizeTexture(ktxTexture* This)
{
assert (This != NULL);
return ktxTexture_calcDataSizeLevels(This, This->numLevels);
}
/**
* @memberof ktxTexture @private
* @~English
* @brief Get information about rows of an uncompresssed texture image at a
* specified level.
*
* For an image at @p level of a ktxTexture provide the number of rows, the
* packed (unpadded) number of bytes in a row and the padding necessary to
* comply with KTX_GL_UNPACK_ALIGNMENT.
*
* @param[in] This pointer to the ktxTexture object of interest.
* @param[in] level level of interest.
* @param[in,out] numRows pointer to location to store the number of rows.
* @param[in,out] pRowLengthBytes pointer to location to store number of bytes
* in a row.
* @param[in.out] pRowPadding pointer to location to store the number of bytes
* of padding.
*/
void
ktxTexture_rowInfo(ktxTexture* This, ktx_uint32_t level,
ktx_uint32_t* numRows, ktx_uint32_t* pRowLengthBytes,
ktx_uint32_t* pRowPadding)
{
DECLARE_PROTECTED(ktxTexture);
struct blockCount {
ktx_uint32_t x;
} blockCount;
assert (This != NULL);
assert(!This->isCompressed);
assert(prtctd->_formatSize.blockWidth == 1U
&& prtctd->_formatSize.blockHeight == 1U
&& prtctd->_formatSize.blockDepth == 1U);
blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth) >> level);
*numRows = MAX(1, (This->baseHeight / prtctd->_formatSize.blockHeight) >> level);
*pRowLengthBytes = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8;
*pRowPadding = padRow(pRowLengthBytes);
}
/**
* @memberof ktxTexture
* @~English
* @brief Return pitch between rows of a texture image level in bytes.
*
* For uncompressed textures the pitch is the number of bytes between
* rows of texels. For compressed textures it is the number of bytes
* between rows of blocks. The value is padded to GL_UNPACK_ALIGNMENT,
* if necessary. For all currently known compressed formats padding
* will not be necessary.
*
* @param[in] This pointer to the ktxTexture object of interest.
* @param[in] level level of interest.
*
* @return the row pitch in bytes.
*/
ktx_uint32_t
ktxTexture_GetRowPitch(ktxTexture* This, ktx_uint32_t level)
{
DECLARE_PROTECTED(ktxTexture)
struct blockCount {
ktx_uint32_t x;
} blockCount;
ktx_uint32_t pitch;
blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth) >> level);
pitch = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8;
(void)padRow(&pitch);
return pitch;
}
/**
* @memberof ktxTexture @private
* @~English
* @brief Query if a ktxTexture has an active stream.
*
* Tests if a ktxTexture has unread image data. The internal stream is closed
* once all the images have been read.
*
* @param[in] This pointer to the ktxTexture object of interest.
*
* @return KTX_TRUE if there is an active stream, KTX_FALSE otherwise.
*/
ktx_bool_t
ktxTexture_isActiveStream(ktxTexture* This)
{
assert(This != NULL);
ktxStream* stream = ktxTexture_getStream(This);
return stream->data.file != NULL;
}
/** @} */
+108
View File
@@ -0,0 +1,108 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2019-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Declare internal ktxTexture functions for sharing between
* compilation units.
*
* These functions are private and should not be used outside the library.
*/
#ifndef _TEXTURE_H_
#define _TEXTURE_H_
#include "ktx.h"
#include "formatsize.h"
#define DECLARE_PRIVATE(class) class ## _private* private = This->_private
#define DECLARE_PROTECTED(class) class ## _protected* prtctd = This->_protected;
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
KTX_FORMAT_VERSION_ONE = 1,
KTX_FORMAT_VERSION_TWO = 2
} ktxFormatVersionEnum;
typedef ktx_size_t (* PFNCALCDATASIZELEVELS)(ktxTexture* This,
ktx_uint32_t levels);
typedef ktx_size_t (* PFNCALCFACELODSIZE)(ktxTexture* This, ktx_uint32_t level);
typedef ktx_size_t (* PFNCALCLEVELOFFSET)(ktxTexture* This, ktx_uint32_t level);
typedef struct ktxTexture_vtblInt {
PFNCALCDATASIZELEVELS calcDataSizeLevels;
PFNCALCFACELODSIZE calcFaceLodSize;
PFNCALCLEVELOFFSET calcLevelOffset;
} ktxTexture_vtblInt;
#define ktxTexture_calcDataSizeLevels(This, levels) \
This->_protected->_vtbl.calcDataSizeLevels(This, levels);
#define ktxTexture_calcFaceLodSize(This, level) \
This->_protected->_vtbl.calcFaceLodSize(This, level);
#define ktxTexture_calcLevelOffset(This, level) \
This->_protected->_vtbl.calcLevelOffset(This, level);
/**
* @memberof ktxTexture
* @~English
*
* @brief protected members of ktxTexture.
*/
typedef struct ktxTexture_protected {
ktxTexture_vtblInt _vtbl;
ktxFormatSize _formatSize;
ktx_uint32_t _typeSize;
ktxStream _stream;
} ktxTexture_protected;
#define ktxTexture_getStream(t) ((ktxStream*)(&(t)->_protected->_stream))
#define ktxTexture1_getStream(t1) ktxTexture_getStream((ktxTexture*)t1)
#define ktxTexture2_getStream(t2) ktxTexture_getStream((ktxTexture*)t2)
KTX_error_code
ktxTexture_iterateLoadedImages(ktxTexture* This, PFNKTXITERCB iterCb,
void* userdata);
KTX_error_code
ktxTexture_iterateSourceImages(ktxTexture* This, PFNKTXITERCB iterCb,
void* userdata);
ktx_size_t ktxTexture_calcDataSizeTexture(ktxTexture* This);
ktx_size_t ktxTexture_calcImageSize(ktxTexture* This, ktx_uint32_t level,
ktxFormatVersionEnum fv);
ktx_bool_t ktxTexture_isActiveStream(ktxTexture* This);
ktx_size_t ktxTexture_calcLevelSize(ktxTexture* This, ktx_uint32_t level,
ktxFormatVersionEnum fv);
ktx_size_t ktxTexture_doCalcFaceLodSize(ktxTexture* This, ktx_uint32_t level,
ktxFormatVersionEnum fv);
ktx_size_t ktxTexture_layerSize(ktxTexture* This, ktx_uint32_t level,
ktxFormatVersionEnum fv);
void ktxTexture_rowInfo(ktxTexture* This, ktx_uint32_t level,
ktx_uint32_t* numRows, ktx_uint32_t* rowBytes,
ktx_uint32_t* rowPadding);
KTX_error_code
ktxTexture_construct(ktxTexture* This,
const ktxTextureCreateInfo* const createInfo,
ktxFormatSize* formatSize);
KTX_error_code
ktxTexture_constructFromStream(ktxTexture* This, ktxStream* pStream,
ktxTextureCreateFlags createFlags);
void
ktxTexture_destruct(ktxTexture* This);
#ifdef __cplusplus
}
#endif
#endif /* _TEXTURE_H_ */
+1496
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2019-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Declare internal ktxTexture1 functions for sharing between
* compilation units.
*
* These functions are private and should not be used outside the library.
*/
#ifndef _TEXTURE1_H_
#define _TEXTURE1_H_
#include "texture.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CLASS ktxTexture1
#include "texture_funcs.inl"
#undef CLASS
KTX_error_code
ktxTexture1_constructFromStreamAndHeader(ktxTexture1* This, ktxStream* pStream,
KTX_header* pHeader,
ktxTextureCreateFlags createFlags);
ktx_uint64_t ktxTexture1_calcDataSizeTexture(ktxTexture1* This);
ktx_size_t ktxTexture1_calcLevelOffset(ktxTexture1* This, ktx_uint32_t level);
ktx_uint32_t ktxTexture1_glTypeSize(ktxTexture1* This);
#ifdef __cplusplus
}
#endif
#endif /* _TEXTURE1_H_ */
+3043
View File
File diff suppressed because it is too large Load Diff
+67
View File
@@ -0,0 +1,67 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2019-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Declare internal ktxTexture2 functions for sharing between
* compilation units.
*
* These functions are private and should not be used outside the library.
*/
#ifndef _TEXTURE2_H_
#define _TEXTURE2_H_
#include "texture.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CLASS ktxTexture2
#include "texture_funcs.inl"
#undef CLASS
typedef struct ktxTexture2_private {
ktx_uint8_t* _supercompressionGlobalData;
ktx_uint32_t _requiredLevelAlignment;
ktx_uint64_t _sgdByteLength;
ktx_uint64_t _firstLevelFileOffset; /*!< Always 0, unless the texture was
created from a stream and the image
data is not yet loaded. */
// Must be last so it can grow.
ktxLevelIndexEntry _levelIndex[1]; /*!< Offsets in this index are from the
start of the image data. Use
ktxTexture_levelStreamOffset() and
ktxTexture_levelDataOffset(). The former
will add the above file offset to the
index offset. */
} ktxTexture2_private;
KTX_error_code
ktxTexture2_constructCopy(ktxTexture2* This, ktxTexture2* orig);
KTX_error_code
ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
KTX_header2* pHeader,
ktxTextureCreateFlags createFlags);
ktx_uint64_t ktxTexture2_calcDataSizeTexture(ktxTexture2* This);
ktx_size_t ktxTexture2_calcLevelOffset(ktxTexture2* This, ktx_uint32_t level);
ktx_uint32_t ktxTexture2_calcRequiredLevelAlignment(ktxTexture2* This);
ktx_uint64_t ktxTexture2_levelFileOffset(ktxTexture2* This, ktx_uint32_t level);
ktx_uint64_t ktxTexture2_levelDataOffset(ktxTexture2* This, ktx_uint32_t level);
#ifdef __cplusplus
}
#endif
#endif /* _TEXTURE2_H_ */
+61
View File
@@ -0,0 +1,61 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2019-2020 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Templates for functions common to base & derived ktxTexture classes.
*
* Define CLASS before including this file.
*/
#define CAT(c, n) PRIMITIVE_CAT(c, n)
#define PRIMITIVE_CAT(c, n) c ## _ ## n
#define CLASS_FUNC(name) CAT(CLASS, name)
/*
======================================
Virtual ktxTexture functions
======================================
*/
ktx_size_t CLASS_FUNC(GetImageSize)(CLASS* This, ktx_uint32_t level);
KTX_error_code CLASS_FUNC(GLUpload)(CLASS* This, GLuint* pTexture,
GLenum* pTarget, GLenum* pGlerror);
KTX_error_code CLASS_FUNC(IterateLevels)(CLASS* This,
PFNKTXITERCB iterCb,
void* userdata);
KTX_error_code CLASS_FUNC(IterateLevelFaces)(CLASS* This,
PFNKTXITERCB iterCb,
void* userdata);
KTX_error_code CLASS_FUNC(IterateLoadLevelFaces)(CLASS* This,
PFNKTXITERCB iterCb,
void* userdata);
KTX_error_code CLASS_FUNC(SetImageFromStdioStream)(CLASS* This,
ktx_uint32_t level,ktx_uint32_t layer,
ktx_uint32_t faceSlice,
FILE* src, ktx_size_t srcSize);
KTX_error_code CLASS_FUNC(SetImageFromMemory)(CLASS* This,
ktx_uint32_t level, ktx_uint32_t layer,
ktx_uint32_t faceSlice,
const ktx_uint8_t* src, ktx_size_t srcSize);
/*
======================================
Internal ktxTexture functions
======================================
*/
void CLASS_FUNC(destruct)(CLASS* This);
+942
View File
@@ -0,0 +1,942 @@
/*
Copyright (c) 2003-2010, Troy D. Hanson http://uthash.sourceforge.net All rights reserved.
SPDX-License-Identifier: BSD-1-Clause
*/
#ifndef UTHASH_H
#define UTHASH_H
#include <string.h> /* memcmp,strlen */
#include <stddef.h> /* ptrdiff_t */
/* These macros use decltype or the earlier __typeof GNU extension.
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
when compiling c++ source) this code uses whatever method is needed
or, for VS2008 where neither is available, uses casting workarounds. */
#ifdef _MSC_VER /* MS compiler */
#if _MSC_VER >= 1600 && __cplusplus /* VS2010 or newer in C++ mode */
#define DECLTYPE(x) (decltype(x))
#else /* VS2008 or older (or VS2010 in C mode) */
#define NO_DECLTYPE
#define DECLTYPE(x)
#endif
#else /* GNU, Sun and other compilers */
#define DECLTYPE(x) (__typeof(x))
#endif
#ifdef NO_DECLTYPE
#define DECLTYPE_ASSIGN(dst,src) \
do { \
char **_da_dst = (char**)(&(dst)); \
*_da_dst = (char*)(src); \
} while(0)
#else
#define DECLTYPE_ASSIGN(dst,src) \
do { \
(dst) = DECLTYPE(dst)(src); \
} while(0)
#endif
/* a number of the hash function use uint32_t which isn't defined on win32 */
#ifdef _MSC_VER
typedef unsigned int uint32_t;
#else
#include <inttypes.h> /* uint32_t */
#endif
#define UTHASH_VERSION 1.9.1
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
#define uthash_free(ptr) free(ptr) /* free fcn */
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
/* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
/* calculate the element whose hash handle address is hhe */
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
#define HASH_FIND(hh,head,keyptr,keylen,out) \
do { \
unsigned _hf_bkt,_hf_hashv; \
out=NULL; \
if (head) { \
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
keyptr,keylen,out); \
} \
} \
} while (0)
#ifdef HASH_BLOOM
#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
#define HASH_BLOOM_MAKE(tbl) \
do { \
(tbl)->bloom_nbits = HASH_BLOOM; \
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
} while (0);
#define HASH_BLOOM_FREE(tbl) \
do { \
uthash_free((tbl)->bloom_bv); \
} while (0);
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
#define HASH_BLOOM_ADD(tbl,hashv) \
HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
#define HASH_BLOOM_TEST(tbl,hashv) \
HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
#else
#define HASH_BLOOM_MAKE(tbl)
#define HASH_BLOOM_FREE(tbl)
#define HASH_BLOOM_ADD(tbl,hashv)
#define HASH_BLOOM_TEST(tbl,hashv) (1)
#endif
#define HASH_MAKE_TABLE(hh,head) \
do { \
(head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
sizeof(UT_hash_table)); \
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
(head)->hh.tbl->tail = &((head)->hh); \
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
memset((head)->hh.tbl->buckets, 0, \
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
HASH_BLOOM_MAKE((head)->hh.tbl); \
(head)->hh.tbl->signature = HASH_SIGNATURE; \
} while(0)
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
do { \
unsigned _ha_bkt; \
(add)->hh.next = NULL; \
(add)->hh.key = (char*)keyptr; \
(add)->hh.keylen = keylen_in; \
if (!(head)) { \
head = (add); \
(head)->hh.prev = NULL; \
HASH_MAKE_TABLE(hh,head); \
} else { \
(head)->hh.tbl->tail->next = (add); \
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
(head)->hh.tbl->tail = &((add)->hh); \
} \
(head)->hh.tbl->num_items++; \
(add)->hh.tbl = (head)->hh.tbl; \
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
(add)->hh.hashv, _ha_bkt); \
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
HASH_FSCK(hh,head); \
} while(0)
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
do { \
bkt = ((hashv) & ((num_bkts) - 1)); \
} while(0)
/* delete "delptr" from the hash table.
* "the usual" patch-up process for the app-order doubly-linked-list.
* The use of _hd_hh_del below deserves special explanation.
* These used to be expressed using (delptr) but that led to a bug
* if someone used the same symbol for the head and deletee, like
* HASH_DELETE(hh,users,users);
* We want that to work, but by changing the head (users) below
* we were forfeiting our ability to further refer to the deletee (users)
* in the patch-up process. Solution: use scratch space to
* copy the deletee pointer, then the latter references are via that
* scratch pointer rather than through the repointed (users) symbol.
*/
#define HASH_DELETE(hh,head,delptr) \
do { \
unsigned _hd_bkt; \
struct UT_hash_handle *_hd_hh_del; \
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
uthash_free((head)->hh.tbl->buckets ); \
HASH_BLOOM_FREE((head)->hh.tbl); \
uthash_free((head)->hh.tbl); \
head = NULL; \
} else { \
_hd_hh_del = &((delptr)->hh); \
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
(head)->hh.tbl->tail = \
(UT_hash_handle*)((char*)((delptr)->hh.prev) + \
(head)->hh.tbl->hho); \
} \
if ((delptr)->hh.prev) { \
((UT_hash_handle*)((char*)((delptr)->hh.prev) + \
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \
} else { \
DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
} \
if (_hd_hh_del->next) { \
((UT_hash_handle*)((char*)_hd_hh_del->next + \
(head)->hh.tbl->hho))->prev = \
_hd_hh_del->prev; \
} \
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
(head)->hh.tbl->num_items--; \
} \
HASH_FSCK(hh,head); \
} while (0)
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
#define HASH_FIND_STR(head,findstr,out) \
HASH_FIND(hh,head,findstr,strlen(findstr),out)
#define HASH_ADD_STR(head,strfield,add) \
HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
#define HASH_FIND_INT(head,findint,out) \
HASH_FIND(hh,head,findint,sizeof(int),out)
#define HASH_ADD_INT(head,intfield,add) \
HASH_ADD(hh,head,intfield,sizeof(int),add)
#define HASH_FIND_PTR(head,findptr,out) \
HASH_FIND(hh,head,findptr,sizeof(void *),out)
#define HASH_ADD_PTR(head,ptrfield,add) \
HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
#define HASH_DEL(head,delptr) \
HASH_DELETE(hh,head,delptr)
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
*/
#ifdef HASH_DEBUG
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
#define HASH_FSCK(hh,head) \
do { \
unsigned _bkt_i; \
unsigned _count, _bkt_count; \
char *_prev; \
struct UT_hash_handle *_thh; \
if (head) { \
_count = 0; \
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
_bkt_count = 0; \
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
_prev = NULL; \
while (_thh) { \
if (_prev != (char*)(_thh->hh_prev)) { \
HASH_OOPS("invalid hh_prev %p, actual %p\n", \
_thh->hh_prev, _prev ); \
} \
_bkt_count++; \
_prev = (char*)(_thh); \
_thh = _thh->hh_next; \
} \
_count += _bkt_count; \
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
HASH_OOPS("invalid bucket count %d, actual %d\n", \
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
} \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("invalid hh item count %d, actual %d\n", \
(head)->hh.tbl->num_items, _count ); \
} \
/* traverse hh in app order; check next/prev integrity, count */ \
_count = 0; \
_prev = NULL; \
_thh = &(head)->hh; \
while (_thh) { \
_count++; \
if (_prev !=(char*)(_thh->prev)) { \
HASH_OOPS("invalid prev %p, actual %p\n", \
_thh->prev, _prev ); \
} \
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
(head)->hh.tbl->hho) : NULL ); \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("invalid app item count %d, actual %d\n", \
(head)->hh.tbl->num_items, _count ); \
} \
} \
} while (0)
#else
#define HASH_FSCK(hh,head)
#endif
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
* the descriptor to which this macro is defined for tuning the hash function.
* The app can #include <unistd.h> to get the prototype for write(2). */
#ifdef HASH_EMIT_KEYS
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
do { \
unsigned _klen = fieldlen; \
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
write(HASH_EMIT_KEYS, keyptr, fieldlen); \
} while (0)
#else
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
#endif
/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
#ifdef HASH_FUNCTION
#define HASH_FCN HASH_FUNCTION
#else
#define HASH_FCN HASH_JEN
#endif
/* The Bernstein hash function, used in Perl prior to v5.6 */
#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _hb_keylen=keylen; \
char *_hb_key=(char*)key; \
(hashv) = 0; \
while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \
bkt = (hashv) & (num_bkts-1); \
} while (0)
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _sx_i; \
char *_hs_key=(char*)key; \
hashv = 0; \
for(_sx_i=0; _sx_i < keylen; _sx_i++) \
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
bkt = hashv & (num_bkts-1); \
} while (0)
#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _fn_i; \
char *_hf_key=(char*)key; \
hashv = 2166136261UL; \
for(_fn_i=0; _fn_i < keylen; _fn_i++) \
hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \
bkt = hashv & (num_bkts-1); \
} while(0);
#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _ho_i; \
char *_ho_key=(char*)key; \
hashv = 0; \
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
hashv += _ho_key[_ho_i]; \
hashv += (hashv << 10); \
hashv ^= (hashv >> 6); \
} \
hashv += (hashv << 3); \
hashv ^= (hashv >> 11); \
hashv += (hashv << 15); \
bkt = hashv & (num_bkts-1); \
} while(0)
#define HASH_JEN_MIX(a,b,c) \
do { \
a -= b; a -= c; a ^= ( c >> 13 ); \
b -= c; b -= a; b ^= ( a << 8 ); \
c -= a; c -= b; c ^= ( b >> 13 ); \
a -= b; a -= c; a ^= ( c >> 12 ); \
b -= c; b -= a; b ^= ( a << 16 ); \
c -= a; c -= b; c ^= ( b >> 5 ); \
a -= b; a -= c; a ^= ( c >> 3 ); \
b -= c; b -= a; b ^= ( a << 10 ); \
c -= a; c -= b; c ^= ( b >> 15 ); \
} while (0)
#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _hj_i,_hj_j,_hj_k; \
char *_hj_key=(char*)key; \
hashv = 0xfeedbeef; \
_hj_i = _hj_j = 0x9e3779b9; \
_hj_k = keylen; \
while (_hj_k >= 12) { \
_hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ ( (unsigned)_hj_key[2] << 16 ) \
+ ( (unsigned)_hj_key[3] << 24 ) ); \
_hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ ( (unsigned)_hj_key[6] << 16 ) \
+ ( (unsigned)_hj_key[7] << 24 ) ); \
hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ ( (unsigned)_hj_key[10] << 16 ) \
+ ( (unsigned)_hj_key[11] << 24 ) ); \
\
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
\
_hj_key += 12; \
_hj_k -= 12; \
} \
hashv += keylen; \
switch ( _hj_k ) { \
case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
case 5: _hj_j += _hj_key[4]; \
case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
case 1: _hj_i += _hj_key[0]; \
} \
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
bkt = hashv & (num_bkts-1); \
} while(0)
/* The Paul Hsieh hash function */
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
#endif
#if !defined (get16bits)
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
do { \
char *_sfh_key=(char*)key; \
uint32_t _sfh_tmp, _sfh_len = keylen; \
\
int _sfh_rem = _sfh_len & 3; \
_sfh_len >>= 2; \
hashv = 0xcafebabe; \
\
/* Main loop */ \
for (;_sfh_len > 0; _sfh_len--) { \
hashv += get16bits (_sfh_key); \
_sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \
hashv = (hashv << 16) ^ _sfh_tmp; \
_sfh_key += 2*sizeof (uint16_t); \
hashv += hashv >> 11; \
} \
\
/* Handle end cases */ \
switch (_sfh_rem) { \
case 3: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 16; \
hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \
hashv += hashv >> 11; \
break; \
case 2: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 11; \
hashv += hashv >> 17; \
break; \
case 1: hashv += *_sfh_key; \
hashv ^= hashv << 10; \
hashv += hashv >> 1; \
} \
\
/* Force "avalanching" of final 127 bits */ \
hashv ^= hashv << 3; \
hashv += hashv >> 5; \
hashv ^= hashv << 4; \
hashv += hashv >> 17; \
hashv ^= hashv << 25; \
hashv += hashv >> 6; \
bkt = hashv & (num_bkts-1); \
} while(0);
#ifdef HASH_USING_NO_STRICT_ALIASING
/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads.
* For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
* So MurmurHash comes in two versions, the faster unaligned one and the slower
* aligned one. We only use the faster one on CPU's where we know it's safe.
*
* Note the preprocessor built-in defines can be emitted using:
*
* gcc -m64 -dM -E - < /dev/null (on gcc)
* cc -## a.c (where a.c is a simple test file) (Sun Studio)
*/
#if (defined(__i386__) || defined(__x86_64__))
#define HASH_MUR HASH_MUR_UNALIGNED
#else
#define HASH_MUR HASH_MUR_ALIGNED
#endif
/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */
#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt) \
do { \
const unsigned int _mur_m = 0x5bd1e995; \
const int _mur_r = 24; \
hashv = 0xcafebabe ^ keylen; \
char *_mur_key = (char *)key; \
uint32_t _mur_tmp, _mur_len = keylen; \
\
for (;_mur_len >= 4; _mur_len-=4) { \
_mur_tmp = *(uint32_t *)_mur_key; \
_mur_tmp *= _mur_m; \
_mur_tmp ^= _mur_tmp >> _mur_r; \
_mur_tmp *= _mur_m; \
hashv *= _mur_m; \
hashv ^= _mur_tmp; \
_mur_key += 4; \
} \
\
switch(_mur_len) \
{ \
case 3: hashv ^= _mur_key[2] << 16; \
case 2: hashv ^= _mur_key[1] << 8; \
case 1: hashv ^= _mur_key[0]; \
hashv *= _mur_m; \
}; \
\
hashv ^= hashv >> 13; \
hashv *= _mur_m; \
hashv ^= hashv >> 15; \
\
bkt = hashv & (num_bkts-1); \
} while(0)
/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */
#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt) \
do { \
const unsigned int _mur_m = 0x5bd1e995; \
const int _mur_r = 24; \
hashv = 0xcafebabe ^ keylen; \
char *_mur_key = (char *)key; \
uint32_t _mur_len = keylen; \
int _mur_align = (int)_mur_key & 3; \
\
if (_mur_align && (_mur_len >= 4)) { \
unsigned _mur_t = 0, _mur_d = 0; \
switch(_mur_align) { \
case 1: _mur_t |= _mur_key[2] << 16; \
case 2: _mur_t |= _mur_key[1] << 8; \
case 3: _mur_t |= _mur_key[0]; \
} \
_mur_t <<= (8 * _mur_align); \
_mur_key += 4-_mur_align; \
_mur_len -= 4-_mur_align; \
int _mur_sl = 8 * (4-_mur_align); \
int _mur_sr = 8 * _mur_align; \
\
for (;_mur_len >= 4; _mur_len-=4) { \
_mur_d = *(unsigned *)_mur_key; \
_mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
unsigned _mur_k = _mur_t; \
_mur_k *= _mur_m; \
_mur_k ^= _mur_k >> _mur_r; \
_mur_k *= _mur_m; \
hashv *= _mur_m; \
hashv ^= _mur_k; \
_mur_t = _mur_d; \
_mur_key += 4; \
} \
_mur_d = 0; \
if(_mur_len >= _mur_align) { \
switch(_mur_align) { \
case 3: _mur_d |= _mur_key[2] << 16; \
case 2: _mur_d |= _mur_key[1] << 8; \
case 1: _mur_d |= _mur_key[0]; \
} \
unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
_mur_k *= _mur_m; \
_mur_k ^= _mur_k >> _mur_r; \
_mur_k *= _mur_m; \
hashv *= _mur_m; \
hashv ^= _mur_k; \
_mur_k += _mur_align; \
_mur_len -= _mur_align; \
\
switch(_mur_len) \
{ \
case 3: hashv ^= _mur_key[2] << 16; \
case 2: hashv ^= _mur_key[1] << 8; \
case 1: hashv ^= _mur_key[0]; \
hashv *= _mur_m; \
} \
} else { \
switch(_mur_len) \
{ \
case 3: _mur_d ^= _mur_key[2] << 16; \
case 2: _mur_d ^= _mur_key[1] << 8; \
case 1: _mur_d ^= _mur_key[0]; \
case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
hashv *= _mur_m; \
} \
} \
\
hashv ^= hashv >> 13; \
hashv *= _mur_m; \
hashv ^= hashv >> 15; \
} else { \
for (;_mur_len >= 4; _mur_len-=4) { \
unsigned _mur_k = *(unsigned*)_mur_key; \
_mur_k *= _mur_m; \
_mur_k ^= _mur_k >> _mur_r; \
_mur_k *= _mur_m; \
hashv *= _mur_m; \
hashv ^= _mur_k; \
_mur_key += 4; \
} \
switch(_mur_len) \
{ \
case 3: hashv ^= _mur_key[2] << 16; \
case 2: hashv ^= _mur_key[1] << 8; \
case 1: hashv ^= _mur_key[0]; \
hashv *= _mur_m; \
} \
\
hashv ^= hashv >> 13; \
hashv *= _mur_m; \
hashv ^= hashv >> 15; \
} \
bkt = hashv & (num_bkts-1); \
} while(0)
#endif /* HASH_USING_NO_STRICT_ALIASING */
/* key comparison function; return 0 if keys equal */
#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
/* iterate over items in a known bucket to find desired item */
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
do { \
if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \
else out=NULL; \
while (out) { \
if (out->hh.keylen == keylen_in) { \
if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \
} \
if (out->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,out->hh.hh_next)); \
else out = NULL; \
} \
} while(0)
/* add an item to a bucket */
#define HASH_ADD_TO_BKT(head,addhh) \
do { \
head.count++; \
(addhh)->hh_next = head.hh_head; \
(addhh)->hh_prev = NULL; \
if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
(head).hh_head=addhh; \
if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
&& (addhh)->tbl->noexpand != 1) { \
HASH_EXPAND_BUCKETS((addhh)->tbl); \
} \
} while(0)
/* remove an item from a given bucket */
#define HASH_DEL_IN_BKT(hh,head,hh_del) \
(head).count--; \
if ((head).hh_head == hh_del) { \
(head).hh_head = hh_del->hh_next; \
} \
if (hh_del->hh_prev) { \
hh_del->hh_prev->hh_next = hh_del->hh_next; \
} \
if (hh_del->hh_next) { \
hh_del->hh_next->hh_prev = hh_del->hh_prev; \
}
/* Bucket expansion has the effect of doubling the number of buckets
* and redistributing the items into the new buckets. Ideally the
* items will distribute more or less evenly into the new buckets
* (the extent to which this is true is a measure of the quality of
* the hash function as it applies to the key domain).
*
* With the items distributed into more buckets, the chain length
* (item count) in each bucket is reduced. Thus by expanding buckets
* the hash keeps a bound on the chain length. This bounded chain
* length is the essence of how a hash provides constant time lookup.
*
* The calculation of tbl->ideal_chain_maxlen below deserves some
* explanation. First, keep in mind that we're calculating the ideal
* maximum chain length based on the *new* (doubled) bucket count.
* In fractions this is just n/b (n=number of items,b=new num buckets).
* Since the ideal chain length is an integer, we want to calculate
* ceil(n/b). We don't depend on floating point arithmetic in this
* hash, so to calculate ceil(n/b) with integers we could write
*
* ceil(n/b) = (n/b) + ((n%b)?1:0)
*
* and in fact a previous version of this hash did just that.
* But now we have improved things a bit by recognizing that b is
* always a power of two. We keep its base 2 log handy (call it lb),
* so now we can write this with a bit shift and logical AND:
*
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
*
*/
#define HASH_EXPAND_BUCKETS(tbl) \
do { \
unsigned _he_bkt; \
unsigned _he_bkt_i; \
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
_he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
memset(_he_new_buckets, 0, \
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
tbl->ideal_chain_maxlen = \
(tbl->num_items >> (tbl->log2_num_buckets+1)) + \
((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
tbl->nonideal_items = 0; \
for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
{ \
_he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
while (_he_thh) { \
_he_hh_nxt = _he_thh->hh_next; \
HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
_he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
tbl->nonideal_items++; \
_he_newbkt->expand_mult = _he_newbkt->count / \
tbl->ideal_chain_maxlen; \
} \
_he_thh->hh_prev = NULL; \
_he_thh->hh_next = _he_newbkt->hh_head; \
if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
_he_thh; \
_he_newbkt->hh_head = _he_thh; \
_he_thh = _he_hh_nxt; \
} \
} \
tbl->num_buckets *= 2; \
tbl->log2_num_buckets++; \
uthash_free( tbl->buckets ); \
tbl->buckets = _he_new_buckets; \
tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
(tbl->ineff_expands+1) : 0; \
if (tbl->ineff_expands > 1) { \
tbl->noexpand=1; \
uthash_noexpand_fyi(tbl); \
} \
uthash_expand_fyi(tbl); \
} while(0)
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
/* Note that HASH_SORT assumes the hash handle name to be hh.
* HASH_SRT was added to allow the hash handle name to be passed in. */
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
#define HASH_SRT(hh,head,cmpfcn) \
do { \
unsigned _hs_i; \
unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
if (head) { \
_hs_insize = 1; \
_hs_looping = 1; \
_hs_list = &((head)->hh); \
while (_hs_looping) { \
_hs_p = _hs_list; \
_hs_list = NULL; \
_hs_tail = NULL; \
_hs_nmerges = 0; \
while (_hs_p) { \
_hs_nmerges++; \
_hs_q = _hs_p; \
_hs_psize = 0; \
for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
_hs_psize++; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
if (! (_hs_q) ) break; \
} \
_hs_qsize = _hs_insize; \
while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
if (_hs_psize == 0) { \
_hs_e = _hs_q; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_qsize--; \
} else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
_hs_e = _hs_p; \
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
((void*)((char*)(_hs_p->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_psize--; \
} else if (( \
cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
) <= 0) { \
_hs_e = _hs_p; \
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
((void*)((char*)(_hs_p->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_psize--; \
} else { \
_hs_e = _hs_q; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_qsize--; \
} \
if ( _hs_tail ) { \
_hs_tail->next = ((_hs_e) ? \
ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
} else { \
_hs_list = _hs_e; \
} \
_hs_e->prev = ((_hs_tail) ? \
ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
_hs_tail = _hs_e; \
} \
_hs_p = _hs_q; \
} \
_hs_tail->next = NULL; \
if ( _hs_nmerges <= 1 ) { \
_hs_looping=0; \
(head)->hh.tbl->tail = _hs_tail; \
DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
} \
_hs_insize *= 2; \
} \
HASH_FSCK(hh,head); \
} \
} while (0)
/* This function selects items from one hash into another hash.
* The end result is that the selected items have dual presence
* in both hashes. There is no copy of the items made; rather
* they are added into the new hash through a secondary hash
* hash handle that must be present in the structure. */
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
do { \
unsigned _src_bkt, _dst_bkt; \
void *_last_elt=NULL, *_elt; \
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
if (src) { \
for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
_src_hh; \
_src_hh = _src_hh->hh_next) { \
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
if (cond(_elt)) { \
_dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
_dst_hh->key = _src_hh->key; \
_dst_hh->keylen = _src_hh->keylen; \
_dst_hh->hashv = _src_hh->hashv; \
_dst_hh->prev = _last_elt; \
_dst_hh->next = NULL; \
if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
if (!dst) { \
DECLTYPE_ASSIGN(dst,_elt); \
HASH_MAKE_TABLE(hh_dst,dst); \
} else { \
_dst_hh->tbl = (dst)->hh_dst.tbl; \
} \
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
(dst)->hh_dst.tbl->num_items++; \
_last_elt = _elt; \
_last_elt_hh = _dst_hh; \
} \
} \
} \
} \
HASH_FSCK(hh_dst,dst); \
} while (0)
#define HASH_CLEAR(hh,head) \
do { \
if (head) { \
uthash_free((head)->hh.tbl->buckets ); \
uthash_free((head)->hh.tbl); \
(head)=NULL; \
} \
} while(0)
/* obtain a count of items in the hash */
#define HASH_COUNT(head) HASH_CNT(hh,head)
#define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0)
typedef struct UT_hash_bucket {
struct UT_hash_handle *hh_head;
unsigned count;
/* expand_mult is normally set to 0. In this situation, the max chain length
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
* the bucket's chain exceeds this length, bucket expansion is triggered).
* However, setting expand_mult to a non-zero value delays bucket expansion
* (that would be triggered by additions to this particular bucket)
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
* (The multiplier is simply expand_mult+1). The whole idea of this
* multiplier is to reduce bucket expansions, since they are expensive, in
* situations where we know that a particular bucket tends to be overused.
* It is better to let its chain length grow to a longer yet-still-bounded
* value, than to do an O(n) bucket expansion too often.
*/
unsigned expand_mult;
} UT_hash_bucket;
/* random signature used only to find hash tables in external analysis */
#define HASH_SIGNATURE 0xa0111fe1
#define HASH_BLOOM_SIGNATURE 0xb12220f2
typedef struct UT_hash_table {
UT_hash_bucket *buckets;
unsigned num_buckets, log2_num_buckets;
unsigned num_items;
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
/* in an ideal situation (all buckets used equally), no bucket would have
* more than ceil(#items/#buckets) items. that's the ideal chain length. */
unsigned ideal_chain_maxlen;
/* nonideal_items is the number of items in the hash whose chain position
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven
* hash distribution; reaching them in a chain traversal takes >ideal steps */
unsigned nonideal_items;
/* ineffective expands occur when a bucket doubling was performed, but
* afterward, more than half the items in the hash had nonideal chain
* positions. If this happens on two consecutive expansions we inhibit any
* further expansion, as it's not helping; this happens when the hash
* function isn't a good fit for the key domain. When expansion is inhibited
* the hash will still work, albeit no longer in constant time. */
unsigned ineff_expands, noexpand;
uint32_t signature; /* used only to find hash tables in external analysis */
#ifdef HASH_BLOOM
uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
uint8_t *bloom_bv;
char bloom_nbits;
#endif
} UT_hash_table;
typedef struct UT_hash_handle {
struct UT_hash_table *tbl;
void *prev; /* prev element in app order */
void *next; /* next element in app order */
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
struct UT_hash_handle *hh_next; /* next hh in bucket order */
void *key; /* ptr to enclosing struct's key */
unsigned keylen; /* enclosing struct's key len */
unsigned hashv; /* result of hash-fcn(key) */
} UT_hash_handle;
#endif /* UTHASH_H */
+11
View File
@@ -0,0 +1,11 @@
/*
// [API version]
@par API Version
v4.0
// [API version]
// [Code version]
exitcode-128-NOTFOUND
// [Code version]
*/
#define LIBKTX_VERSION exitcode-128-NOTFOUND
#define LIBKTX_DEFAULT_VERSION v4.0.__default__
+132
View File
@@ -0,0 +1,132 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2024 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Get GL format information matching a VkFormat
*
*/
#ifndef _VK2GL_H_
#define _VK2GL_H_
#include "vkformat_enum.h"
#include "GL/glcorearb.h"
// These are only in glext.h.
#if !defined( GL_COMPRESSED_SRGB_S3TC_DXT1_EXT )
#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
#endif
#if !defined( GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT )
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
#endif
#if !defined( GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT )
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
#endif
#if !defined( GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT )
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
#endif
// These are only in GLES headers not in glcorearb.h or glext.h.
//
// ETC
//
#if !defined( GL_ETC1_RGB8_OES )
#define GL_ETC1_RGB8_OES 0x8D64
#endif
//
// PVRTC
//
#if !defined( GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG )
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
#endif
#if !defined( GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG )
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG 0x9137
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG 0x9138
#endif
#if !defined( GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT )
#define GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT 0x8A54
#define GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT 0x8A55
#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT 0x8A56
#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT 0x8A57
#endif
#if !defined( GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG )
#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0
#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1
#endif
//
// ASTC
//
#if !defined( GL_COMPRESSED_RGBA_ASTC_3x3x3_OES )
#define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES 0x93C0
#define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES 0x93C1
#define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES 0x93C2
#define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES 0x93C3
#define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES 0x93C4
#define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES 0x93C5
#define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES 0x93C6
#define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES 0x93C7
#define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES 0x93C8
#define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES 0x93C9
#endif
#if !defined( GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES )
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES 0x93E0
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES 0x93E1
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES 0x93E2
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES 0x93E3
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES 0x93E4
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES 0x93E5
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES 0x93E6
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES 0x93E7
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES 0x93E8
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES 0x93E9
#endif
static inline GLenum vkFormat2glInternalFormat( VkFormat vkFormat )
{
switch ( vkFormat )
{
#include "vkFormat2glInternalFormat.inl"
default: return GL_INVALID_VALUE;
}
}
static inline GLenum vkFormat2glFormat( VkFormat vkFormat )
{
switch ( vkFormat )
{
#include "vkFormat2glFormat.inl"
default: return GL_INVALID_VALUE;
}
}
static inline GLenum vkFormat2glType( VkFormat vkFormat )
{
switch ( vkFormat )
{
#include "vkFormat2glType.inl"
default: return GL_INVALID_VALUE;
}
}
#endif /* _VK2GL_H_ */
+96
View File
@@ -0,0 +1,96 @@
// Copyright 2020 The Khronos Group Inc.
// SPDX-License-Identifier: Apache-2.0
/*************************************** Do not edit ***************************************
Automatically generated by
https://github.com/KhronosGroup/KTX-Specification/blob/master/generate_format_switches.rb
*******************************************************************************************/
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return GL_RGBA;
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return GL_BGRA;
case VK_FORMAT_R5G6B5_UNORM_PACK16: return GL_RGB;
case VK_FORMAT_B5G6R5_UNORM_PACK16: return GL_RGB;
case VK_FORMAT_R5G5B5A1_UNORM_PACK16: return GL_RGBA;
case VK_FORMAT_B5G5R5A1_UNORM_PACK16: return GL_BGRA;
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: return GL_BGRA;
case VK_FORMAT_R8_UNORM: return GL_RED;
case VK_FORMAT_R8_SNORM: return GL_RED;
case VK_FORMAT_R8_UINT: return GL_RED_INTEGER;
case VK_FORMAT_R8_SINT: return GL_RED_INTEGER;
case VK_FORMAT_R8_SRGB: return GL_RED;
case VK_FORMAT_R8G8_UNORM: return GL_RG;
case VK_FORMAT_R8G8_SNORM: return GL_RG;
case VK_FORMAT_R8G8_UINT: return GL_RG_INTEGER;
case VK_FORMAT_R8G8_SINT: return GL_RG_INTEGER;
case VK_FORMAT_R8G8_SRGB: return GL_RG;
case VK_FORMAT_R8G8B8_UNORM: return GL_RGB;
case VK_FORMAT_R8G8B8_SNORM: return GL_RGB;
case VK_FORMAT_R8G8B8_UINT: return GL_RGB_INTEGER;
case VK_FORMAT_R8G8B8_SINT: return GL_RGB_INTEGER;
case VK_FORMAT_R8G8B8_SRGB: return GL_RGB;
case VK_FORMAT_B8G8R8_UNORM: return GL_BGR;
case VK_FORMAT_B8G8R8_SNORM: return GL_BGR;
case VK_FORMAT_B8G8R8_UINT: return GL_BGR_INTEGER;
case VK_FORMAT_B8G8R8_SINT: return GL_BGR_INTEGER;
case VK_FORMAT_B8G8R8_SRGB: return GL_BGR;
case VK_FORMAT_R8G8B8A8_UNORM: return GL_RGBA;
case VK_FORMAT_R8G8B8A8_SNORM: return GL_RGBA;
case VK_FORMAT_R8G8B8A8_UINT: return GL_RGBA_INTEGER;
case VK_FORMAT_R8G8B8A8_SINT: return GL_RGBA_INTEGER;
case VK_FORMAT_R8G8B8A8_SRGB: return GL_RGBA;
case VK_FORMAT_B8G8R8A8_UNORM: return GL_BGRA;
case VK_FORMAT_B8G8R8A8_SNORM: return GL_BGRA;
case VK_FORMAT_B8G8R8A8_UINT: return GL_BGRA_INTEGER;
case VK_FORMAT_B8G8R8A8_SINT: return GL_BGRA_INTEGER;
case VK_FORMAT_B8G8R8A8_SRGB: return GL_BGRA;
case VK_FORMAT_A8B8G8R8_UNORM_PACK32: return GL_RGBA;
case VK_FORMAT_A8B8G8R8_SNORM_PACK32: return GL_RGBA;
case VK_FORMAT_A8B8G8R8_UINT_PACK32: return GL_RGBA_INTEGER;
case VK_FORMAT_A8B8G8R8_SINT_PACK32: return GL_RGBA_INTEGER;
case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return GL_RGBA;
case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return GL_BGRA;
case VK_FORMAT_A2R10G10B10_UINT_PACK32: return GL_BGRA_INTEGER;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return GL_RGBA;
case VK_FORMAT_A2B10G10R10_UINT_PACK32: return GL_RGBA_INTEGER;
case VK_FORMAT_R16_UNORM: return GL_RED;
case VK_FORMAT_R16_SNORM: return GL_RED;
case VK_FORMAT_R16_UINT: return GL_RED_INTEGER;
case VK_FORMAT_R16_SINT: return GL_RED_INTEGER;
case VK_FORMAT_R16_SFLOAT: return GL_RED;
case VK_FORMAT_R16G16_UNORM: return GL_RG;
case VK_FORMAT_R16G16_SNORM: return GL_RG;
case VK_FORMAT_R16G16_UINT: return GL_RG_INTEGER;
case VK_FORMAT_R16G16_SINT: return GL_RG_INTEGER;
case VK_FORMAT_R16G16_SFLOAT: return GL_RG;
case VK_FORMAT_R16G16B16_UNORM: return GL_RGB;
case VK_FORMAT_R16G16B16_SNORM: return GL_RGB;
case VK_FORMAT_R16G16B16_UINT: return GL_RGB_INTEGER;
case VK_FORMAT_R16G16B16_SINT: return GL_RGB_INTEGER;
case VK_FORMAT_R16G16B16_SFLOAT: return GL_RGB;
case VK_FORMAT_R16G16B16A16_UNORM: return GL_RGBA;
case VK_FORMAT_R16G16B16A16_SNORM: return GL_RGBA;
case VK_FORMAT_R16G16B16A16_UINT: return GL_RGBA_INTEGER;
case VK_FORMAT_R16G16B16A16_SINT: return GL_RGBA_INTEGER;
case VK_FORMAT_R16G16B16A16_SFLOAT: return GL_RGBA;
case VK_FORMAT_R32_UINT: return GL_RED_INTEGER;
case VK_FORMAT_R32_SINT: return GL_RED_INTEGER;
case VK_FORMAT_R32_SFLOAT: return GL_RED;
case VK_FORMAT_R32G32_UINT: return GL_RG_INTEGER;
case VK_FORMAT_R32G32_SINT: return GL_RG_INTEGER;
case VK_FORMAT_R32G32_SFLOAT: return GL_RG;
case VK_FORMAT_R32G32B32_UINT: return GL_RGB_INTEGER;
case VK_FORMAT_R32G32B32_SINT: return GL_RGB_INTEGER;
case VK_FORMAT_R32G32B32_SFLOAT: return GL_RGB;
case VK_FORMAT_R32G32B32A32_UINT: return GL_RGBA_INTEGER;
case VK_FORMAT_R32G32B32A32_SINT: return GL_RGBA_INTEGER;
case VK_FORMAT_R32G32B32A32_SFLOAT: return GL_RGBA;
case VK_FORMAT_B10G11R11_UFLOAT_PACK32: return GL_RGB;
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: return GL_RGB;
case VK_FORMAT_D16_UNORM: return GL_DEPTH_COMPONENT;
case VK_FORMAT_D32_SFLOAT: return GL_DEPTH_COMPONENT;
case VK_FORMAT_S8_UINT: return GL_STENCIL_INDEX;
case VK_FORMAT_D24_UNORM_S8_UINT: return GL_DEPTH_STENCIL;
case VK_FORMAT_D32_SFLOAT_S8_UINT: return GL_DEPTH_STENCIL;
case VK_FORMAT_A4R4G4B4_UNORM_PACK16: return GL_BGRA;
case VK_FORMAT_A4B4G4R4_UNORM_PACK16: return GL_RGBA;
case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR: return GL_RGBA;
case VK_FORMAT_A8_UNORM_KHR: return GL_ALPHA;
+202
View File
@@ -0,0 +1,202 @@
// Copyright 2020 The Khronos Group Inc.
// SPDX-License-Identifier: Apache-2.0
/*************************************** Do not edit ***************************************
Automatically generated by
https://github.com/KhronosGroup/KTX-Specification/blob/master/generate_format_switches.rb
*******************************************************************************************/
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return GL_RGBA4;
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return GL_RGBA4;
case VK_FORMAT_R5G6B5_UNORM_PACK16: return GL_RGB565;
case VK_FORMAT_B5G6R5_UNORM_PACK16: return GL_RGB565;
case VK_FORMAT_R5G5B5A1_UNORM_PACK16: return GL_RGB5_A1;
case VK_FORMAT_B5G5R5A1_UNORM_PACK16: return GL_RGB5_A1;
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: return GL_RGB5_A1;
case VK_FORMAT_R8_UNORM: return GL_R8;
case VK_FORMAT_R8_SNORM: return GL_R8_SNORM;
case VK_FORMAT_R8_UINT: return GL_R8UI;
case VK_FORMAT_R8_SINT: return GL_R8I;
case VK_FORMAT_R8_SRGB: return GL_SR8_EXT;
case VK_FORMAT_R8G8_UNORM: return GL_RG8;
case VK_FORMAT_R8G8_SNORM: return GL_RG8_SNORM;
case VK_FORMAT_R8G8_UINT: return GL_RG8UI;
case VK_FORMAT_R8G8_SINT: return GL_RG8I;
case VK_FORMAT_R8G8_SRGB: return GL_SRG8_EXT;
case VK_FORMAT_R8G8B8_UNORM: return GL_RGB8;
case VK_FORMAT_R8G8B8_SNORM: return GL_RGB8_SNORM;
case VK_FORMAT_R8G8B8_UINT: return GL_RGB8UI;
case VK_FORMAT_R8G8B8_SINT: return GL_RGB8I;
case VK_FORMAT_R8G8B8_SRGB: return GL_SRGB8;
case VK_FORMAT_B8G8R8_UNORM: return GL_RGB8;
case VK_FORMAT_B8G8R8_SNORM: return GL_RGB8_SNORM;
case VK_FORMAT_B8G8R8_UINT: return GL_RGB8UI;
case VK_FORMAT_B8G8R8_SINT: return GL_RGB8I;
case VK_FORMAT_B8G8R8_SRGB: return GL_SRGB8;
case VK_FORMAT_R8G8B8A8_UNORM: return GL_RGBA8;
case VK_FORMAT_R8G8B8A8_SNORM: return GL_RGBA8_SNORM;
case VK_FORMAT_R8G8B8A8_UINT: return GL_RGBA8UI;
case VK_FORMAT_R8G8B8A8_SINT: return GL_RGBA8I;
case VK_FORMAT_R8G8B8A8_SRGB: return GL_SRGB8_ALPHA8;
case VK_FORMAT_B8G8R8A8_UNORM: return GL_RGBA8;
case VK_FORMAT_B8G8R8A8_SNORM: return GL_RGBA8_SNORM;
case VK_FORMAT_B8G8R8A8_UINT: return GL_RGBA8UI;
case VK_FORMAT_B8G8R8A8_SINT: return GL_RGBA8I;
case VK_FORMAT_B8G8R8A8_SRGB: return GL_SRGB8_ALPHA8;
case VK_FORMAT_A8B8G8R8_UNORM_PACK32: return GL_RGBA8;
case VK_FORMAT_A8B8G8R8_SNORM_PACK32: return GL_RGBA8_SNORM;
case VK_FORMAT_A8B8G8R8_UINT_PACK32: return GL_RGBA8UI;
case VK_FORMAT_A8B8G8R8_SINT_PACK32: return GL_RGBA8I;
case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return GL_SRGB8_ALPHA8;
case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return GL_RGB10_A2;
case VK_FORMAT_A2R10G10B10_UINT_PACK32: return GL_RGB10_A2UI;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return GL_RGB10_A2;
case VK_FORMAT_A2B10G10R10_UINT_PACK32: return GL_RGB10_A2UI;
case VK_FORMAT_R16_UNORM: return GL_R16;
case VK_FORMAT_R16_SNORM: return GL_R16_SNORM;
case VK_FORMAT_R16_UINT: return GL_R16UI;
case VK_FORMAT_R16_SINT: return GL_R16I;
case VK_FORMAT_R16_SFLOAT: return GL_R16F;
case VK_FORMAT_R16G16_UNORM: return GL_RG16;
case VK_FORMAT_R16G16_SNORM: return GL_RG16_SNORM;
case VK_FORMAT_R16G16_UINT: return GL_RG16UI;
case VK_FORMAT_R16G16_SINT: return GL_RG16I;
case VK_FORMAT_R16G16_SFLOAT: return GL_RG16F;
case VK_FORMAT_R16G16B16_UNORM: return GL_RGB16;
case VK_FORMAT_R16G16B16_SNORM: return GL_RGB16_SNORM;
case VK_FORMAT_R16G16B16_UINT: return GL_RGB16UI;
case VK_FORMAT_R16G16B16_SINT: return GL_RGB16I;
case VK_FORMAT_R16G16B16_SFLOAT: return GL_RGB16F;
case VK_FORMAT_R16G16B16A16_UNORM: return GL_RGBA16;
case VK_FORMAT_R16G16B16A16_SNORM: return GL_RGBA16_SNORM;
case VK_FORMAT_R16G16B16A16_UINT: return GL_RGBA16UI;
case VK_FORMAT_R16G16B16A16_SINT: return GL_RGBA16I;
case VK_FORMAT_R16G16B16A16_SFLOAT: return GL_RGBA16F;
case VK_FORMAT_R32_UINT: return GL_R32UI;
case VK_FORMAT_R32_SINT: return GL_R32I;
case VK_FORMAT_R32_SFLOAT: return GL_R32F;
case VK_FORMAT_R32G32_UINT: return GL_RG32UI;
case VK_FORMAT_R32G32_SINT: return GL_RG32I;
case VK_FORMAT_R32G32_SFLOAT: return GL_RG32F;
case VK_FORMAT_R32G32B32_UINT: return GL_RGB32UI;
case VK_FORMAT_R32G32B32_SINT: return GL_RGB32I;
case VK_FORMAT_R32G32B32_SFLOAT: return GL_RGB32F;
case VK_FORMAT_R32G32B32A32_UINT: return GL_RGBA32UI;
case VK_FORMAT_R32G32B32A32_SINT: return GL_RGBA32I;
case VK_FORMAT_R32G32B32A32_SFLOAT: return GL_RGBA32F;
case VK_FORMAT_B10G11R11_UFLOAT_PACK32: return GL_R11F_G11F_B10F;
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: return GL_RGB9_E5;
case VK_FORMAT_D16_UNORM: return GL_DEPTH_COMPONENT16;
case VK_FORMAT_D32_SFLOAT: return GL_DEPTH_COMPONENT32F;
case VK_FORMAT_S8_UINT: return GL_STENCIL_INDEX8;
case VK_FORMAT_D24_UNORM_S8_UINT: return GL_DEPTH24_STENCIL8;
case VK_FORMAT_D32_SFLOAT_S8_UINT: return GL_DEPTH32F_STENCIL8;
case VK_FORMAT_BC1_RGB_UNORM_BLOCK: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
case VK_FORMAT_BC1_RGB_SRGB_BLOCK: return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
case VK_FORMAT_BC2_UNORM_BLOCK: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
case VK_FORMAT_BC2_SRGB_BLOCK: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
case VK_FORMAT_BC3_UNORM_BLOCK: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case VK_FORMAT_BC3_SRGB_BLOCK: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
case VK_FORMAT_BC4_UNORM_BLOCK: return GL_COMPRESSED_RED_RGTC1;
case VK_FORMAT_BC4_SNORM_BLOCK: return GL_COMPRESSED_SIGNED_RED_RGTC1;
case VK_FORMAT_BC5_UNORM_BLOCK: return GL_COMPRESSED_RG_RGTC2;
case VK_FORMAT_BC5_SNORM_BLOCK: return GL_COMPRESSED_SIGNED_RG_RGTC2;
case VK_FORMAT_BC6H_UFLOAT_BLOCK: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
case VK_FORMAT_BC6H_SFLOAT_BLOCK: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
case VK_FORMAT_BC7_UNORM_BLOCK: return GL_COMPRESSED_RGBA_BPTC_UNORM;
case VK_FORMAT_BC7_SRGB_BLOCK: return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: return GL_COMPRESSED_RGB8_ETC2;
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ETC2;
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2;
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: return GL_COMPRESSED_RGBA8_ETC2_EAC;
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
case VK_FORMAT_EAC_R11_UNORM_BLOCK: return GL_COMPRESSED_R11_EAC;
case VK_FORMAT_EAC_R11_SNORM_BLOCK: return GL_COMPRESSED_SIGNED_R11_EAC;
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: return GL_COMPRESSED_RG11_EAC;
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: return GL_COMPRESSED_SIGNED_RG11_EAC;
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR;
case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_5x4_KHR;
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR;
case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_5x4_KHR;
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_5x5_KHR;
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR;
case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_5x5_KHR;
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_6x5_KHR;
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR;
case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_6x5_KHR;
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_6x6_KHR;
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR;
case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_6x6_KHR;
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_8x5_KHR;
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR;
case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_8x5_KHR;
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_8x6_KHR;
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR;
case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_8x6_KHR;
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_8x8_KHR;
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR;
case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_8x8_KHR;
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x5_KHR;
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR;
case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x5_KHR;
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x6_KHR;
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR;
case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x6_KHR;
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x8_KHR;
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR;
case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x8_KHR;
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x10_KHR;
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR;
case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_10x10_KHR;
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_12x10_KHR;
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR;
case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_12x10_KHR;
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: return GL_COMPRESSED_RGBA_ASTC_12x12_KHR;
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR;
case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK: return GL_COMPRESSED_RGBA_ASTC_12x12_KHR;
case VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_3x3x3_OES;
case VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES;
case VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_3x3x3_OES;
case VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_4x3x3_OES;
case VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES;
case VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_4x3x3_OES;
case VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_4x4x3_OES;
case VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES;
case VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_4x4x3_OES;
case VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_4x4x4_OES;
case VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES;
case VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_4x4x4_OES;
case VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_5x4x4_OES;
case VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES;
case VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_5x4x4_OES;
case VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_5x5x4_OES;
case VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES;
case VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_5x5x4_OES;
case VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_5x5x5_OES;
case VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES;
case VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_5x5x5_OES;
case VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_6x5x5_OES;
case VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES;
case VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_6x5x5_OES;
case VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_6x6x5_OES;
case VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES;
case VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_6x6x5_OES;
case VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_6x6x6_OES;
case VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES;
case VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT: return GL_COMPRESSED_RGBA_ASTC_6x6x6_OES;
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: return GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG;
case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: return GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG;
case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT;
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT;
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG;
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG;
case VK_FORMAT_A4R4G4B4_UNORM_PACK16: return GL_RGBA4;
case VK_FORMAT_A4B4G4R4_UNORM_PACK16: return GL_RGBA4;
case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR: return GL_RGB5_A1;
case VK_FORMAT_A8_UNORM_KHR: return GL_ALPHA8_EXT;
+96
View File
@@ -0,0 +1,96 @@
// Copyright 2020 The Khronos Group Inc.
// SPDX-License-Identifier: Apache-2.0
/*************************************** Do not edit ***************************************
Automatically generated by
https://github.com/KhronosGroup/KTX-Specification/blob/master/generate_format_switches.rb
*******************************************************************************************/
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return GL_UNSIGNED_SHORT_4_4_4_4;
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return GL_UNSIGNED_SHORT_4_4_4_4;
case VK_FORMAT_R5G6B5_UNORM_PACK16: return GL_UNSIGNED_SHORT_5_6_5;
case VK_FORMAT_B5G6R5_UNORM_PACK16: return GL_UNSIGNED_SHORT_5_6_5_REV;
case VK_FORMAT_R5G5B5A1_UNORM_PACK16: return GL_UNSIGNED_SHORT_5_5_5_1;
case VK_FORMAT_B5G5R5A1_UNORM_PACK16: return GL_UNSIGNED_SHORT_5_5_5_1;
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: return GL_UNSIGNED_SHORT_1_5_5_5_REV;
case VK_FORMAT_R8_UNORM: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8_SNORM: return GL_BYTE;
case VK_FORMAT_R8_UINT: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8_SINT: return GL_BYTE;
case VK_FORMAT_R8_SRGB: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8G8_UNORM: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8G8_SNORM: return GL_BYTE;
case VK_FORMAT_R8G8_UINT: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8G8_SINT: return GL_BYTE;
case VK_FORMAT_R8G8_SRGB: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8G8B8_UNORM: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8G8B8_SNORM: return GL_BYTE;
case VK_FORMAT_R8G8B8_UINT: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8G8B8_SINT: return GL_BYTE;
case VK_FORMAT_R8G8B8_SRGB: return GL_UNSIGNED_BYTE;
case VK_FORMAT_B8G8R8_UNORM: return GL_UNSIGNED_BYTE;
case VK_FORMAT_B8G8R8_SNORM: return GL_BYTE;
case VK_FORMAT_B8G8R8_UINT: return GL_UNSIGNED_BYTE;
case VK_FORMAT_B8G8R8_SINT: return GL_BYTE;
case VK_FORMAT_B8G8R8_SRGB: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8G8B8A8_UNORM: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8G8B8A8_SNORM: return GL_BYTE;
case VK_FORMAT_R8G8B8A8_UINT: return GL_UNSIGNED_BYTE;
case VK_FORMAT_R8G8B8A8_SINT: return GL_BYTE;
case VK_FORMAT_R8G8B8A8_SRGB: return GL_UNSIGNED_BYTE;
case VK_FORMAT_B8G8R8A8_UNORM: return GL_UNSIGNED_BYTE;
case VK_FORMAT_B8G8R8A8_SNORM: return GL_BYTE;
case VK_FORMAT_B8G8R8A8_UINT: return GL_UNSIGNED_BYTE;
case VK_FORMAT_B8G8R8A8_SINT: return GL_BYTE;
case VK_FORMAT_B8G8R8A8_SRGB: return GL_UNSIGNED_BYTE;
case VK_FORMAT_A8B8G8R8_UNORM_PACK32: return GL_UNSIGNED_BYTE;
case VK_FORMAT_A8B8G8R8_SNORM_PACK32: return GL_BYTE;
case VK_FORMAT_A8B8G8R8_UINT_PACK32: return GL_UNSIGNED_BYTE;
case VK_FORMAT_A8B8G8R8_SINT_PACK32: return GL_BYTE;
case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return GL_UNSIGNED_BYTE;
case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return GL_UNSIGNED_INT_2_10_10_10_REV;
case VK_FORMAT_A2R10G10B10_UINT_PACK32: return GL_UNSIGNED_INT_2_10_10_10_REV;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return GL_UNSIGNED_INT_2_10_10_10_REV;
case VK_FORMAT_A2B10G10R10_UINT_PACK32: return GL_UNSIGNED_INT_2_10_10_10_REV;
case VK_FORMAT_R16_UNORM: return GL_UNSIGNED_SHORT;
case VK_FORMAT_R16_SNORM: return GL_SHORT;
case VK_FORMAT_R16_UINT: return GL_UNSIGNED_SHORT;
case VK_FORMAT_R16_SINT: return GL_SHORT;
case VK_FORMAT_R16_SFLOAT: return GL_HALF_FLOAT;
case VK_FORMAT_R16G16_UNORM: return GL_UNSIGNED_SHORT;
case VK_FORMAT_R16G16_SNORM: return GL_SHORT;
case VK_FORMAT_R16G16_UINT: return GL_UNSIGNED_SHORT;
case VK_FORMAT_R16G16_SINT: return GL_SHORT;
case VK_FORMAT_R16G16_SFLOAT: return GL_HALF_FLOAT;
case VK_FORMAT_R16G16B16_UNORM: return GL_UNSIGNED_SHORT;
case VK_FORMAT_R16G16B16_SNORM: return GL_SHORT;
case VK_FORMAT_R16G16B16_UINT: return GL_UNSIGNED_SHORT;
case VK_FORMAT_R16G16B16_SINT: return GL_SHORT;
case VK_FORMAT_R16G16B16_SFLOAT: return GL_HALF_FLOAT;
case VK_FORMAT_R16G16B16A16_UNORM: return GL_UNSIGNED_SHORT;
case VK_FORMAT_R16G16B16A16_SNORM: return GL_SHORT;
case VK_FORMAT_R16G16B16A16_UINT: return GL_UNSIGNED_SHORT;
case VK_FORMAT_R16G16B16A16_SINT: return GL_SHORT;
case VK_FORMAT_R16G16B16A16_SFLOAT: return GL_HALF_FLOAT;
case VK_FORMAT_R32_UINT: return GL_UNSIGNED_INT;
case VK_FORMAT_R32_SINT: return GL_INT;
case VK_FORMAT_R32_SFLOAT: return GL_FLOAT;
case VK_FORMAT_R32G32_UINT: return GL_UNSIGNED_INT;
case VK_FORMAT_R32G32_SINT: return GL_INT;
case VK_FORMAT_R32G32_SFLOAT: return GL_FLOAT;
case VK_FORMAT_R32G32B32_UINT: return GL_UNSIGNED_INT;
case VK_FORMAT_R32G32B32_SINT: return GL_INT;
case VK_FORMAT_R32G32B32_SFLOAT: return GL_FLOAT;
case VK_FORMAT_R32G32B32A32_UINT: return GL_UNSIGNED_INT;
case VK_FORMAT_R32G32B32A32_SINT: return GL_INT;
case VK_FORMAT_R32G32B32A32_SFLOAT: return GL_FLOAT;
case VK_FORMAT_B10G11R11_UFLOAT_PACK32: return GL_UNSIGNED_INT_10F_11F_11F_REV;
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: return GL_UNSIGNED_INT_5_9_9_9_REV;
case VK_FORMAT_D16_UNORM: return GL_UNSIGNED_SHORT;
case VK_FORMAT_D32_SFLOAT: return GL_FLOAT;
case VK_FORMAT_S8_UINT: return GL_UNSIGNED_BYTE;
case VK_FORMAT_D24_UNORM_S8_UINT: return GL_UNSIGNED_INT_24_8;
case VK_FORMAT_D32_SFLOAT_S8_UINT: return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
case VK_FORMAT_A4R4G4B4_UNORM_PACK16: return GL_UNSIGNED_SHORT_4_4_4_4_REV;
case VK_FORMAT_A4B4G4R4_UNORM_PACK16: return GL_UNSIGNED_SHORT_4_4_4_4_REV;
case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR: return GL_UNSIGNED_SHORT_1_5_5_5_REV;
case VK_FORMAT_A8_UNORM_KHR: return GL_UNSIGNED_BYTE;
+1392
View File
File diff suppressed because it is too large Load Diff
+130
View File
@@ -0,0 +1,130 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2017-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Retrieve Vulkan function pointers needed by libktx
*/
#define UNIX 0
#define MACOS 0
#define WINDOWS 0
#define IOS 0
#if defined(_WIN32)
#undef WINDOWS
#define WINDOWS 1
#endif
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__)
#undef UNIX
#define UNIX 1
#endif
#if defined(linux) || defined(__linux) || defined(__linux__)
#undef UNIX
#define UNIX 1
#endif
#if defined(__APPLE__) && defined(__x86_64__)
#undef MACOS
#define MACOS 1
#endif
#if defined(__APPLE__) && (defined(__arm64__) || defined (__arm__))
#undef IOS
#define IOS 1
#endif
#if (IOS + MACOS + UNIX + WINDOWS) > 1
#error "Multiple OS\'s defined"
#endif
#include "vk_funcs.h"
#if WINDOWS
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#else
#include <dlfcn.h>
#include <stdlib.h>
#endif
#include "ktx.h"
#if WINDOWS
HMODULE ktxVulkanModuleHandle;
#define GetVulkanModuleHandle(flags) ktxGetVulkanModuleHandle()
#define LoadProcAddr GetProcAddress
#elif MACOS || UNIX || IOS
// Using NULL returns a handle that can be used to search the process that
// loaded us and any other libraries it has loaded. That's all we need to
// search as the app is responsible for creating the GL context so it must
// be there.
#define GetVulkanModuleHandle(flags) dlopen(NULL, flags)
#define LoadProcAddr dlsym
void* ktxVulkanModuleHandle;
#else
#error "Don\'t know how to load symbols on this OS."
#endif
#if WINDOWS
#define VULKANLIB "vulkan-1.dll"
static HMODULE
ktxGetVulkanModuleHandle()
{
HMODULE module = NULL;
GetModuleHandleExA(
0,
VULKANLIB,
&module
);
return module;
}
#endif
ktx_error_code_e
ktxLoadVulkanLibrary(void)
{
if (ktxVulkanModuleHandle)
return KTX_SUCCESS;
ktxVulkanModuleHandle = GetVulkanModuleHandle(RTLD_LAZY);
if (ktxVulkanModuleHandle == NULL) {
fprintf(stderr, "Vulkan lib not linked or loaded by application.\n");
// Normal use is for this constructor to be called by an
// application that has completed OpenGL initialization. In that
// case the only cause for failure would be a coding error in our
// library loading. The only other cause would be an application
// calling GLUpload without having initialized OpenGL.
#if defined(DEBUG)
abort();
#else
return KTX_LIBRARY_NOT_LINKED; // So release version doesn't crash.
#endif
}
return KTX_SUCCESS;
}
PFN_vkVoidFunction
ktxLoadVulkanFunction(const char* pName) {
ktx_error_code_e rc = ktxLoadVulkanLibrary();
if (rc != KTX_SUCCESS) {
return NULL;
}
PFN_vkVoidFunction pfn
= (PFN_vkVoidFunction)LoadProcAddr(ktxVulkanModuleHandle, pName);
if (pfn == NULL) {
fprintf(stderr, "Couldn't load Vulkan command: %s\n", pName);
return NULL;
}
return pfn;
}
+48
View File
@@ -0,0 +1,48 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab textwidth=70: */
/*
* Copyright 2017-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Declare pointers for Vulkan functions.
*
* Dynamically retrieving pointers avoids apps having to make sure a
* Vulkan library is available when using a shared libktx, even if
* not using libktx's Vulkan loader.
*/
#ifndef _VK_FUNCS_H_
#define _VK_FUNCS_H_
#if !defined(VK_NO_PROTOTYPES)
#define VK_NO_PROTOTYPES
#endif
#include "vulkan/vk_platform.h"
#include "vulkan/vulkan_core.h"
#include "ktx.h"
#if WINDOWS
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
extern HMODULE ktxVulkanModuleHandle;
#else
extern void* ktxVulkanModuleHandle;
#endif
ktx_error_code_e ktxLoadVulkanLibrary(void);
// This is used to load instance functions through libktx's methods.
PFN_vkVoidFunction ktxLoadVulkanFunction(const char* pName);
#endif /* _VK_FUNCS_H_ */
+215
View File
@@ -0,0 +1,215 @@
/***************************** Do not edit. *****************************
Automatically generated from vulkan_core.h version 287 by mkvkformatfiles.
*************************************************************************/
/*
** Copyright 2015-2024 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
*/
// clang-format off: CI is complicated if formatting checks on generated files are enforced.
#include <stdint.h>
#include <stdbool.h>
#include "vkformat_enum.h"
bool
isProhibitedFormat(VkFormat format)
{
switch (format) {
case VK_FORMAT_R8_USCALED:
case VK_FORMAT_R8_SSCALED:
case VK_FORMAT_R8G8_USCALED:
case VK_FORMAT_R8G8_SSCALED:
case VK_FORMAT_R8G8B8_USCALED:
case VK_FORMAT_R8G8B8_SSCALED:
case VK_FORMAT_B8G8R8_USCALED:
case VK_FORMAT_B8G8R8_SSCALED:
case VK_FORMAT_R8G8B8A8_USCALED:
case VK_FORMAT_R8G8B8A8_SSCALED:
case VK_FORMAT_B8G8R8A8_USCALED:
case VK_FORMAT_B8G8R8A8_SSCALED:
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
case VK_FORMAT_R16_USCALED:
case VK_FORMAT_R16_SSCALED:
case VK_FORMAT_R16G16_USCALED:
case VK_FORMAT_R16G16_SSCALED:
case VK_FORMAT_R16G16B16_USCALED:
case VK_FORMAT_R16G16B16_SSCALED:
case VK_FORMAT_R16G16B16A16_USCALED:
case VK_FORMAT_R16G16B16A16_SSCALED:
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_444_UNORM:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G16_B16R16_2PLANE_444_UNORM:
return true;
default:
return false;
}
}
bool
isSrgbFormat(VkFormat format)
{
switch(format) {
case VK_FORMAT_R8_SRGB:
case VK_FORMAT_R8G8_SRGB:
case VK_FORMAT_R8G8B8_SRGB:
case VK_FORMAT_B8G8R8_SRGB:
case VK_FORMAT_R8G8B8A8_SRGB:
case VK_FORMAT_B8G8R8A8_SRGB:
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
case VK_FORMAT_BC2_SRGB_BLOCK:
case VK_FORMAT_BC3_SRGB_BLOCK:
case VK_FORMAT_BC7_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT:
return true;
default:
return false;
}
}
bool
isValidFormat(VkFormat format)
{
// On MSVC VkFormat can be a signed integer
if ((uint32_t) format <= VK_FORMAT_MAX_STANDARD_ENUM)
return true;
else switch(format) {
case VK_FORMAT_G8B8G8R8_422_UNORM:
case VK_FORMAT_B8G8R8G8_422_UNORM:
case VK_FORMAT_R10X6_UNORM_PACK16:
case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
case VK_FORMAT_R12X4_UNORM_PACK16:
case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
case VK_FORMAT_G16B16G16R16_422_UNORM:
case VK_FORMAT_B16G16R16G16_422_UNORM:
case VK_FORMAT_A4R4G4B4_UNORM_PACK16:
case VK_FORMAT_A4B4G4R4_UNORM_PACK16:
case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK:
case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK:
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT:
case VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT:
case VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT:
case VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT:
case VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT:
case VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT:
case VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT:
case VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT:
case VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT:
case VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT:
case VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT:
case VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT:
case VK_FORMAT_R16G16_SFIXED5_NV:
case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR:
case VK_FORMAT_A8_UNORM_KHR:
return true;
default:
return false;
}
}
// clang-format on
+171
View File
@@ -0,0 +1,171 @@
/*
** Copyright 2025 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include "vkformat_enum.h"
bool
isNotSrgbFormatButHasSrgbVariant(VkFormat format)
{
switch (format) {
// VK_FORMAT_R8_SRGB
case VK_FORMAT_R8_UNORM:
case VK_FORMAT_R8_SNORM:
case VK_FORMAT_R8_UINT:
case VK_FORMAT_R8_SINT:
// VK_FORMAT_R8G8_SRGB
case VK_FORMAT_R8G8_UNORM:
case VK_FORMAT_R8G8_SNORM:
case VK_FORMAT_R8G8_UINT:
case VK_FORMAT_R8G8_SINT:
// VK_FORMAT_R8G8B8_SRGB
case VK_FORMAT_R8G8B8_UNORM:
case VK_FORMAT_R8G8B8_SNORM:
case VK_FORMAT_R8G8B8_UINT:
case VK_FORMAT_R8G8B8_SINT:
// VK_FORMAT_B8G8R8_SRGB
case VK_FORMAT_B8G8R8_UNORM:
case VK_FORMAT_B8G8R8_SNORM:
case VK_FORMAT_B8G8R8_UINT:
case VK_FORMAT_B8G8R8_SINT:
// VK_FORMAT_R8G8B8A8_SRGB
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SNORM:
case VK_FORMAT_R8G8B8A8_UINT:
case VK_FORMAT_R8G8B8A8_SINT:
// VK_FORMAT_B8G8R8A8_SRGB
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SNORM:
case VK_FORMAT_B8G8R8A8_UINT:
case VK_FORMAT_B8G8R8A8_SINT:
// VK_FORMAT_A8B8G8R8_SRGB_PACK32
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
// VK_FORMAT_BC1_RGB_SRGB_BLOCK
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
// VK_FORMAT_BC1_RGBA_SRGB_BLOCK
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
// VK_FORMAT_BC2_SRGB_BLOCK
case VK_FORMAT_BC2_UNORM_BLOCK:
// VK_FORMAT_BC3_SRGB_BLOCK
case VK_FORMAT_BC3_UNORM_BLOCK:
// VK_FORMAT_BC7_SRGB_BLOCK
case VK_FORMAT_BC7_UNORM_BLOCK:
// VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
// VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
// VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
// VK_FORMAT_ASTC_4x4_SRGB_BLOCK
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
// VK_FORMAT_ASTC_5x4_SRGB_BLOCK
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
// VK_FORMAT_ASTC_5x5_SRGB_BLOCK
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
// VK_FORMAT_ASTC_6x5_SRGB_BLOCK
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
// VK_FORMAT_ASTC_6x6_SRGB_BLOCK
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
// VK_FORMAT_ASTC_8x5_SRGB_BLOCK
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
// VK_FORMAT_ASTC_8x6_SRGB_BLOCK
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
// VK_FORMAT_ASTC_8x8_SRGB_BLOCK
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
// VK_FORMAT_ASTC_10x5_SRGB_BLOCK
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
// VK_FORMAT_ASTC_10x6_SRGB_BLOCK
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
// VK_FORMAT_ASTC_10x8_SRGB_BLOCK
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
// VK_FORMAT_ASTC_10x10_SRGB_BLOCK
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
// VK_FORMAT_ASTC_12x10_SRGB_BLOCK
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
// VK_FORMAT_ASTC_12x12_SRGB_BLOCK
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
// VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
// VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
// VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
// VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG
case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
// VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT:
// VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT:
// VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT:
// VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT:
// VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT:
// VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT:
// VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT:
// VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT:
// VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT:
// VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT
case VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT:
return true;
default:
return false;
}
}
+316
View File
@@ -0,0 +1,316 @@
// clang-format off: CI is complicated if formatting checks on generated files are enforced.
#if !defined(_VKFORMAT_ENUM_H_) && !defined(VULKAN_CORE_H_)
#define _VKFORMAT_ENUM_H_
/***************************** Do not edit. *****************************
Automatically generated from vulkan_core.h version 287 by mkvkformatfiles.
*************************************************************************/
/*
** Copyright 2015-2024 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
*/
#if defined(_MSC_VER) && _MSC_VER < 1900 // Older than VS 2015.
typedef unsigned __int32 VkFlags;
#else
#include <stdint.h>
typedef uint32_t VkFlags;
#endif
typedef enum VkFormat {
VK_FORMAT_UNDEFINED = 0,
VK_FORMAT_R4G4_UNORM_PACK8 = 1,
VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2,
VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3,
VK_FORMAT_R5G6B5_UNORM_PACK16 = 4,
VK_FORMAT_B5G6R5_UNORM_PACK16 = 5,
VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6,
VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7,
VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8,
VK_FORMAT_R8_UNORM = 9,
VK_FORMAT_R8_SNORM = 10,
VK_FORMAT_R8_USCALED = 11,
VK_FORMAT_R8_SSCALED = 12,
VK_FORMAT_R8_UINT = 13,
VK_FORMAT_R8_SINT = 14,
VK_FORMAT_R8_SRGB = 15,
VK_FORMAT_R8G8_UNORM = 16,
VK_FORMAT_R8G8_SNORM = 17,
VK_FORMAT_R8G8_USCALED = 18,
VK_FORMAT_R8G8_SSCALED = 19,
VK_FORMAT_R8G8_UINT = 20,
VK_FORMAT_R8G8_SINT = 21,
VK_FORMAT_R8G8_SRGB = 22,
VK_FORMAT_R8G8B8_UNORM = 23,
VK_FORMAT_R8G8B8_SNORM = 24,
VK_FORMAT_R8G8B8_USCALED = 25,
VK_FORMAT_R8G8B8_SSCALED = 26,
VK_FORMAT_R8G8B8_UINT = 27,
VK_FORMAT_R8G8B8_SINT = 28,
VK_FORMAT_R8G8B8_SRGB = 29,
VK_FORMAT_B8G8R8_UNORM = 30,
VK_FORMAT_B8G8R8_SNORM = 31,
VK_FORMAT_B8G8R8_USCALED = 32,
VK_FORMAT_B8G8R8_SSCALED = 33,
VK_FORMAT_B8G8R8_UINT = 34,
VK_FORMAT_B8G8R8_SINT = 35,
VK_FORMAT_B8G8R8_SRGB = 36,
VK_FORMAT_R8G8B8A8_UNORM = 37,
VK_FORMAT_R8G8B8A8_SNORM = 38,
VK_FORMAT_R8G8B8A8_USCALED = 39,
VK_FORMAT_R8G8B8A8_SSCALED = 40,
VK_FORMAT_R8G8B8A8_UINT = 41,
VK_FORMAT_R8G8B8A8_SINT = 42,
VK_FORMAT_R8G8B8A8_SRGB = 43,
VK_FORMAT_B8G8R8A8_UNORM = 44,
VK_FORMAT_B8G8R8A8_SNORM = 45,
VK_FORMAT_B8G8R8A8_USCALED = 46,
VK_FORMAT_B8G8R8A8_SSCALED = 47,
VK_FORMAT_B8G8R8A8_UINT = 48,
VK_FORMAT_B8G8R8A8_SINT = 49,
VK_FORMAT_B8G8R8A8_SRGB = 50,
VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51,
VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52,
VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53,
VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54,
VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55,
VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56,
VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57,
VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58,
VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59,
VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60,
VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61,
VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62,
VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63,
VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64,
VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65,
VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66,
VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67,
VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68,
VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69,
VK_FORMAT_R16_UNORM = 70,
VK_FORMAT_R16_SNORM = 71,
VK_FORMAT_R16_USCALED = 72,
VK_FORMAT_R16_SSCALED = 73,
VK_FORMAT_R16_UINT = 74,
VK_FORMAT_R16_SINT = 75,
VK_FORMAT_R16_SFLOAT = 76,
VK_FORMAT_R16G16_UNORM = 77,
VK_FORMAT_R16G16_SNORM = 78,
VK_FORMAT_R16G16_USCALED = 79,
VK_FORMAT_R16G16_SSCALED = 80,
VK_FORMAT_R16G16_UINT = 81,
VK_FORMAT_R16G16_SINT = 82,
VK_FORMAT_R16G16_SFLOAT = 83,
VK_FORMAT_R16G16B16_UNORM = 84,
VK_FORMAT_R16G16B16_SNORM = 85,
VK_FORMAT_R16G16B16_USCALED = 86,
VK_FORMAT_R16G16B16_SSCALED = 87,
VK_FORMAT_R16G16B16_UINT = 88,
VK_FORMAT_R16G16B16_SINT = 89,
VK_FORMAT_R16G16B16_SFLOAT = 90,
VK_FORMAT_R16G16B16A16_UNORM = 91,
VK_FORMAT_R16G16B16A16_SNORM = 92,
VK_FORMAT_R16G16B16A16_USCALED = 93,
VK_FORMAT_R16G16B16A16_SSCALED = 94,
VK_FORMAT_R16G16B16A16_UINT = 95,
VK_FORMAT_R16G16B16A16_SINT = 96,
VK_FORMAT_R16G16B16A16_SFLOAT = 97,
VK_FORMAT_R32_UINT = 98,
VK_FORMAT_R32_SINT = 99,
VK_FORMAT_R32_SFLOAT = 100,
VK_FORMAT_R32G32_UINT = 101,
VK_FORMAT_R32G32_SINT = 102,
VK_FORMAT_R32G32_SFLOAT = 103,
VK_FORMAT_R32G32B32_UINT = 104,
VK_FORMAT_R32G32B32_SINT = 105,
VK_FORMAT_R32G32B32_SFLOAT = 106,
VK_FORMAT_R32G32B32A32_UINT = 107,
VK_FORMAT_R32G32B32A32_SINT = 108,
VK_FORMAT_R32G32B32A32_SFLOAT = 109,
VK_FORMAT_R64_UINT = 110,
VK_FORMAT_R64_SINT = 111,
VK_FORMAT_R64_SFLOAT = 112,
VK_FORMAT_R64G64_UINT = 113,
VK_FORMAT_R64G64_SINT = 114,
VK_FORMAT_R64G64_SFLOAT = 115,
VK_FORMAT_R64G64B64_UINT = 116,
VK_FORMAT_R64G64B64_SINT = 117,
VK_FORMAT_R64G64B64_SFLOAT = 118,
VK_FORMAT_R64G64B64A64_UINT = 119,
VK_FORMAT_R64G64B64A64_SINT = 120,
VK_FORMAT_R64G64B64A64_SFLOAT = 121,
VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122,
VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123,
VK_FORMAT_D16_UNORM = 124,
VK_FORMAT_X8_D24_UNORM_PACK32 = 125,
VK_FORMAT_D32_SFLOAT = 126,
VK_FORMAT_S8_UINT = 127,
VK_FORMAT_D16_UNORM_S8_UINT = 128,
VK_FORMAT_D24_UNORM_S8_UINT = 129,
VK_FORMAT_D32_SFLOAT_S8_UINT = 130,
VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131,
VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132,
VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133,
VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134,
VK_FORMAT_BC2_UNORM_BLOCK = 135,
VK_FORMAT_BC2_SRGB_BLOCK = 136,
VK_FORMAT_BC3_UNORM_BLOCK = 137,
VK_FORMAT_BC3_SRGB_BLOCK = 138,
VK_FORMAT_BC4_UNORM_BLOCK = 139,
VK_FORMAT_BC4_SNORM_BLOCK = 140,
VK_FORMAT_BC5_UNORM_BLOCK = 141,
VK_FORMAT_BC5_SNORM_BLOCK = 142,
VK_FORMAT_BC6H_UFLOAT_BLOCK = 143,
VK_FORMAT_BC6H_SFLOAT_BLOCK = 144,
VK_FORMAT_BC7_UNORM_BLOCK = 145,
VK_FORMAT_BC7_SRGB_BLOCK = 146,
VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147,
VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148,
VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149,
VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150,
VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151,
VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152,
VK_FORMAT_EAC_R11_UNORM_BLOCK = 153,
VK_FORMAT_EAC_R11_SNORM_BLOCK = 154,
VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155,
VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156,
VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157,
VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158,
VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159,
VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160,
VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161,
VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162,
VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163,
VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164,
VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165,
VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166,
VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167,
VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168,
VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169,
VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170,
VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171,
VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172,
VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173,
VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174,
VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175,
VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176,
VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177,
VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178,
VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179,
VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180,
VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181,
VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182,
VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183,
VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184,
VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000,
VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001,
VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002,
VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003,
VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004,
VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005,
VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006,
VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007,
VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008,
VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009,
VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010,
VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011,
VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012,
VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013,
VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014,
VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015,
VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016,
VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017,
VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018,
VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019,
VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020,
VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021,
VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022,
VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023,
VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024,
VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025,
VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026,
VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027,
VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028,
VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029,
VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030,
VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031,
VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032,
VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033,
VK_FORMAT_G8_B8R8_2PLANE_444_UNORM = 1000330000,
VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16 = 1000330001,
VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16 = 1000330002,
VK_FORMAT_G16_B16R16_2PLANE_444_UNORM = 1000330003,
VK_FORMAT_A4R4G4B4_UNORM_PACK16 = 1000340000,
VK_FORMAT_A4B4G4R4_UNORM_PACK16 = 1000340001,
VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000,
VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001,
VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002,
VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003,
VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004,
VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005,
VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006,
VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK = 1000066007,
VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK = 1000066008,
VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK = 1000066009,
VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK = 1000066010,
VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK = 1000066011,
VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK = 1000066012,
VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK = 1000066013,
VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000,
VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001,
VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002,
VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003,
VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004,
VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT = 1000288000,
VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT = 1000288001,
VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT = 1000288002,
VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT = 1000288003,
VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT = 1000288004,
VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT = 1000288005,
VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT = 1000288006,
VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT = 1000288007,
VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT = 1000288008,
VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT = 1000288009,
VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT = 1000288010,
VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT = 1000288011,
VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT = 1000288012,
VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT = 1000288013,
VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT = 1000288014,
VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT = 1000288015,
VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT = 1000288016,
VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT = 1000288017,
VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT = 1000288018,
VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT = 1000288019,
VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT = 1000288020,
VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT = 1000288021,
VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT = 1000288022,
VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT = 1000288023,
VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT = 1000288024,
VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT = 1000288025,
VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT = 1000288026,
VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT = 1000288027,
VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT = 1000288028,
VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT = 1000288029,
VK_FORMAT_R16G16_SFIXED5_NV = 1000464000,
VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR = 1000470000,
VK_FORMAT_A8_UNORM_KHR = 1000470001,
VK_FORMAT_MAX_ENUM = 0x7FFFFFFF
} VkFormat;
#if defined(_MSC_VER) && _MSC_VER < 1900 // Older than VS 2015.
typedef unsigned __int32 VkFlags;
#else
#include <stdint.h>
typedef uint64_t VkFlags64;
#endif
#define VK_FORMAT_MAX_STANDARD_ENUM 184
#endif /* _VKFORMAT_ENUM_H_ */
// clang-format on
File diff suppressed because it is too large Load Diff
+586
View File
@@ -0,0 +1,586 @@
/***************************** Do not edit. *****************************
Automatically generated from vulkan_core.h version 287 by mkvkformatfiles.
*************************************************************************/
/*
** Copyright 2015-2024 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
*/
// clang-format off: CI is complicated if formatting checks on generated files are enforced.
#include <stdint.h>
#include "vkformat_enum.h"
uint32_t
vkFormatTypeSize(VkFormat format)
{
switch (format) {
case VK_FORMAT_UNDEFINED:
return 1;
case VK_FORMAT_R4G4_UNORM_PACK8:
return 1;
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
return 2;
case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
return 2;
case VK_FORMAT_R5G6B5_UNORM_PACK16:
return 2;
case VK_FORMAT_B5G6R5_UNORM_PACK16:
return 2;
case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
return 2;
case VK_FORMAT_B5G5R5A1_UNORM_PACK16:
return 2;
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
return 2;
case VK_FORMAT_R8_UNORM:
return 1;
case VK_FORMAT_R8_SNORM:
return 1;
case VK_FORMAT_R8_USCALED:
return 1;
case VK_FORMAT_R8_SSCALED:
return 1;
case VK_FORMAT_R8_UINT:
return 1;
case VK_FORMAT_R8_SINT:
return 1;
case VK_FORMAT_R8_SRGB:
return 1;
case VK_FORMAT_R8G8_UNORM:
return 1;
case VK_FORMAT_R8G8_SNORM:
return 1;
case VK_FORMAT_R8G8_USCALED:
return 1;
case VK_FORMAT_R8G8_SSCALED:
return 1;
case VK_FORMAT_R8G8_UINT:
return 1;
case VK_FORMAT_R8G8_SINT:
return 1;
case VK_FORMAT_R8G8_SRGB:
return 1;
case VK_FORMAT_R8G8B8_UNORM:
return 1;
case VK_FORMAT_R8G8B8_SNORM:
return 1;
case VK_FORMAT_R8G8B8_USCALED:
return 1;
case VK_FORMAT_R8G8B8_SSCALED:
return 1;
case VK_FORMAT_R8G8B8_UINT:
return 1;
case VK_FORMAT_R8G8B8_SINT:
return 1;
case VK_FORMAT_R8G8B8_SRGB:
return 1;
case VK_FORMAT_B8G8R8_UNORM:
return 1;
case VK_FORMAT_B8G8R8_SNORM:
return 1;
case VK_FORMAT_B8G8R8_USCALED:
return 1;
case VK_FORMAT_B8G8R8_SSCALED:
return 1;
case VK_FORMAT_B8G8R8_UINT:
return 1;
case VK_FORMAT_B8G8R8_SINT:
return 1;
case VK_FORMAT_B8G8R8_SRGB:
return 1;
case VK_FORMAT_R8G8B8A8_UNORM:
return 1;
case VK_FORMAT_R8G8B8A8_SNORM:
return 1;
case VK_FORMAT_R8G8B8A8_USCALED:
return 1;
case VK_FORMAT_R8G8B8A8_SSCALED:
return 1;
case VK_FORMAT_R8G8B8A8_UINT:
return 1;
case VK_FORMAT_R8G8B8A8_SINT:
return 1;
case VK_FORMAT_R8G8B8A8_SRGB:
return 1;
case VK_FORMAT_B8G8R8A8_UNORM:
return 1;
case VK_FORMAT_B8G8R8A8_SNORM:
return 1;
case VK_FORMAT_B8G8R8A8_USCALED:
return 1;
case VK_FORMAT_B8G8R8A8_SSCALED:
return 1;
case VK_FORMAT_B8G8R8A8_UINT:
return 1;
case VK_FORMAT_B8G8R8A8_SINT:
return 1;
case VK_FORMAT_B8G8R8A8_SRGB:
return 1;
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
return 4;
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
return 4;
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
return 4;
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
return 4;
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
return 4;
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
return 4;
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
return 4;
case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
return 4;
case VK_FORMAT_A2R10G10B10_SNORM_PACK32:
return 4;
case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
return 4;
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
return 4;
case VK_FORMAT_A2R10G10B10_UINT_PACK32:
return 4;
case VK_FORMAT_A2R10G10B10_SINT_PACK32:
return 4;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
return 4;
case VK_FORMAT_A2B10G10R10_SNORM_PACK32:
return 4;
case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
return 4;
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
return 4;
case VK_FORMAT_A2B10G10R10_UINT_PACK32:
return 4;
case VK_FORMAT_A2B10G10R10_SINT_PACK32:
return 4;
case VK_FORMAT_R16_UNORM:
return 2;
case VK_FORMAT_R16_SNORM:
return 2;
case VK_FORMAT_R16_USCALED:
return 2;
case VK_FORMAT_R16_SSCALED:
return 2;
case VK_FORMAT_R16_UINT:
return 2;
case VK_FORMAT_R16_SINT:
return 2;
case VK_FORMAT_R16_SFLOAT:
return 2;
case VK_FORMAT_R16G16_UNORM:
return 2;
case VK_FORMAT_R16G16_SNORM:
return 2;
case VK_FORMAT_R16G16_USCALED:
return 2;
case VK_FORMAT_R16G16_SSCALED:
return 2;
case VK_FORMAT_R16G16_UINT:
return 2;
case VK_FORMAT_R16G16_SINT:
return 2;
case VK_FORMAT_R16G16_SFLOAT:
return 2;
case VK_FORMAT_R16G16B16_UNORM:
return 2;
case VK_FORMAT_R16G16B16_SNORM:
return 2;
case VK_FORMAT_R16G16B16_USCALED:
return 2;
case VK_FORMAT_R16G16B16_SSCALED:
return 2;
case VK_FORMAT_R16G16B16_UINT:
return 2;
case VK_FORMAT_R16G16B16_SINT:
return 2;
case VK_FORMAT_R16G16B16_SFLOAT:
return 2;
case VK_FORMAT_R16G16B16A16_UNORM:
return 2;
case VK_FORMAT_R16G16B16A16_SNORM:
return 2;
case VK_FORMAT_R16G16B16A16_USCALED:
return 2;
case VK_FORMAT_R16G16B16A16_SSCALED:
return 2;
case VK_FORMAT_R16G16B16A16_UINT:
return 2;
case VK_FORMAT_R16G16B16A16_SINT:
return 2;
case VK_FORMAT_R16G16B16A16_SFLOAT:
return 2;
case VK_FORMAT_R32_UINT:
return 4;
case VK_FORMAT_R32_SINT:
return 4;
case VK_FORMAT_R32_SFLOAT:
return 4;
case VK_FORMAT_R32G32_UINT:
return 4;
case VK_FORMAT_R32G32_SINT:
return 4;
case VK_FORMAT_R32G32_SFLOAT:
return 4;
case VK_FORMAT_R32G32B32_UINT:
return 4;
case VK_FORMAT_R32G32B32_SINT:
return 4;
case VK_FORMAT_R32G32B32_SFLOAT:
return 4;
case VK_FORMAT_R32G32B32A32_UINT:
return 4;
case VK_FORMAT_R32G32B32A32_SINT:
return 4;
case VK_FORMAT_R32G32B32A32_SFLOAT:
return 4;
case VK_FORMAT_R64_UINT:
return 8;
case VK_FORMAT_R64_SINT:
return 8;
case VK_FORMAT_R64_SFLOAT:
return 8;
case VK_FORMAT_R64G64_UINT:
return 8;
case VK_FORMAT_R64G64_SINT:
return 8;
case VK_FORMAT_R64G64_SFLOAT:
return 8;
case VK_FORMAT_R64G64B64_UINT:
return 8;
case VK_FORMAT_R64G64B64_SINT:
return 8;
case VK_FORMAT_R64G64B64_SFLOAT:
return 8;
case VK_FORMAT_R64G64B64A64_UINT:
return 8;
case VK_FORMAT_R64G64B64A64_SINT:
return 8;
case VK_FORMAT_R64G64B64A64_SFLOAT:
return 8;
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
return 4;
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
return 4;
case VK_FORMAT_D16_UNORM:
return 2;
case VK_FORMAT_X8_D24_UNORM_PACK32:
return 4;
case VK_FORMAT_D32_SFLOAT:
return 4;
case VK_FORMAT_S8_UINT:
return 1;
case VK_FORMAT_D16_UNORM_S8_UINT:
return 2;
case VK_FORMAT_D24_UNORM_S8_UINT:
return 4;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return 4;
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
return 1;
case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
return 1;
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
return 1;
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
return 1;
case VK_FORMAT_BC2_UNORM_BLOCK:
return 1;
case VK_FORMAT_BC2_SRGB_BLOCK:
return 1;
case VK_FORMAT_BC3_UNORM_BLOCK:
return 1;
case VK_FORMAT_BC3_SRGB_BLOCK:
return 1;
case VK_FORMAT_BC4_UNORM_BLOCK:
return 1;
case VK_FORMAT_BC4_SNORM_BLOCK:
return 1;
case VK_FORMAT_BC5_UNORM_BLOCK:
return 1;
case VK_FORMAT_BC5_SNORM_BLOCK:
return 1;
case VK_FORMAT_BC6H_UFLOAT_BLOCK:
return 1;
case VK_FORMAT_BC6H_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_BC7_UNORM_BLOCK:
return 1;
case VK_FORMAT_BC7_SRGB_BLOCK:
return 1;
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
return 1;
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
return 1;
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
return 1;
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
return 1;
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
return 1;
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
return 1;
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
return 1;
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
return 1;
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
return 1;
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
return 1;
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
return 1;
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
return 1;
case VK_FORMAT_G8B8G8R8_422_UNORM:
return 1;
case VK_FORMAT_B8G8R8G8_422_UNORM:
return 1;
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
return 1;
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
return 1;
case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
return 1;
case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
return 1;
case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
return 1;
case VK_FORMAT_R10X6_UNORM_PACK16:
return 2;
case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
return 2;
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
return 2;
case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
return 2;
case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
return 2;
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
return 2;
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
return 2;
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
return 2;
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
return 2;
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
return 2;
case VK_FORMAT_R12X4_UNORM_PACK16:
return 2;
case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
return 2;
case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
return 2;
case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
return 2;
case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
return 2;
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
return 2;
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
return 2;
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
return 2;
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
return 2;
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
return 2;
case VK_FORMAT_G16B16G16R16_422_UNORM:
return 2;
case VK_FORMAT_B16G16R16G16_422_UNORM:
return 2;
case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
return 2;
case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
return 2;
case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
return 2;
case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
return 2;
case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
return 2;
case VK_FORMAT_G8_B8R8_2PLANE_444_UNORM:
return 1;
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16:
return 2;
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16:
return 2;
case VK_FORMAT_G16_B16R16_2PLANE_444_UNORM:
return 2;
case VK_FORMAT_A4R4G4B4_UNORM_PACK16:
return 2;
case VK_FORMAT_A4B4G4R4_UNORM_PACK16:
return 2;
case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK:
return 1;
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
return 1;
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
return 1;
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
return 1;
case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
return 1;
case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
return 1;
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
return 1;
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
return 1;
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
return 1;
case VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT:
return 1;
case VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT:
return 1;
case VK_FORMAT_R16G16_SFIXED5_NV:
return 2;
case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR:
return 2;
case VK_FORMAT_A8_UNORM_KHR:
return 1;
default:
return 0;
}
}
// clang-format on
+1983
View File
File diff suppressed because it is too large Load Diff
+978
View File
@@ -0,0 +1,978 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/**
* @internal
* @file
* @~English
*
* @brief Functions for creating KTX-format files from a set of images.
*
* @author Mark Callow, HI Corporation
*/
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__GNUC__)
#include <strings.h> // For strncasecmp on GNU/Linux
#endif
#include "ktx.h"
#include "ktxint.h"
#include "filestream.h"
#include "memstream.h"
#include "texture1.h"
#include "dfdutils/dfd.h"
#include "vkformat_enum.h"
#include "vk_format.h"
#include "version.h"
#if defined(_MSC_VER)
#define strncasecmp _strnicmp
#endif
/**
* @defgroup writer Writer
* @brief Write KTX-formatted data.
* @{
*/
/**
* @memberof ktxTexture @private
* @~English
* @brief Set image for level, layer, faceSlice from a ktxStream source.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] level mip level of the image to set.
* @param[in] layer array layer of the image to set.
* @param[in] faceSlice cube map face or depth slice of the image to set.
* @param[in] src ktxStream pointer to the source.
* @param[in] srcSize size of the source image in bytes.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p src is NULL.
* @exception KTX_INVALID_VALUE @p srcSize != the expected image size for the
* specified level, layer & faceSlice.
* @exception KTX_INVALID_OPERATION
* No storage was allocated when the texture was
* created.
*/
KTX_error_code
ktxTexture1_setImageFromStream(ktxTexture1* This, ktx_uint32_t level,
ktx_uint32_t layer, ktx_uint32_t faceSlice,
ktxStream* src, ktx_size_t srcSize)
{
ktx_uint32_t packedRowBytes, rowBytes, rowPadding, numRows = 0;
ktx_size_t packedBytes, unpackedBytes;
ktx_size_t imageOffset;
#if (KTX_GL_UNPACK_ALIGNMENT != 4)
ktx_uint32_t faceLodPadding;
#endif
if (!This || !src)
return KTX_INVALID_VALUE;
if (!This->pData)
return KTX_INVALID_OPERATION;
ktxTexture_GetImageOffset(ktxTexture(This), level, layer, faceSlice, &imageOffset);
if (This->isCompressed) {
packedBytes = ktxTexture_GetImageSize(ktxTexture(This), level);
rowPadding = 0;
// These 2 are not used when rowPadding == 0. Quiets compiler warning.
packedRowBytes = 0;
rowBytes = 0;
} else {
ktxTexture_rowInfo(ktxTexture(This), level, &numRows, &rowBytes, &rowPadding);
unpackedBytes = rowBytes * numRows;
if (rowPadding) {
packedRowBytes = rowBytes - rowPadding;
packedBytes = packedRowBytes * numRows;
} else {
packedRowBytes = rowBytes;
packedBytes = unpackedBytes;
}
}
if (srcSize != packedBytes)
return KTX_INVALID_OPERATION;
// The above will catch a flagrantly invalid srcSize. This is an
// additional check of the internal calculations.
assert (imageOffset + srcSize <= This->dataSize);
#if (KTX_GL_UNPACK_ALIGNMENT != 4)
faceLodPadding = _KTX_PAD4_LEN(faceLodSize);
#endif
if (rowPadding == 0) {
/* Can copy whole image at once */
src->read(src, This->pData + imageOffset, srcSize);
} else {
/* Copy the rows individually, padding each one */
ktx_uint32_t row;
ktx_uint8_t* dst = This->pData + imageOffset;
ktx_uint8_t pad[4] = { 0, 0, 0, 0 };
for (row = 0; row < numRows; row++) {
ktx_uint32_t rowOffset = rowBytes * row;
src->read(src, dst + rowOffset, packedRowBytes);
memcpy(dst + rowOffset + packedRowBytes, pad, rowPadding);
}
}
#if (KTX_GL_UNPACK_ALIGNMENT != 4)
/*
* When KTX_GL_UNPACK_ALIGNMENT == 4, rows, and therefore everything else,
* are always 4-byte aligned and faceLodPadding is always 0. It is always
* 0 for compressed formats too because they all have multiple-of-4 block
* sizes.
*/
if (faceLodPadding)
memcpy(This->pData + faceLodSize, pad, faceLodPadding);
#endif
return KTX_SUCCESS;
}
/**
* @memberof ktxTexture1
* @~English
* @brief Set image for level, layer, faceSlice from a stdio stream source.
*
* Uncompressed images read from the stream are expected to have their rows
* tightly packed as is the norm for most image file formats. The copied image
* is padded as necessary to achieve the KTX-specified row alignment. No
* padding is done if the ktxTexture's @c isCompressed field is @c KTX_TRUE.
*
* Level, layer, faceSlice rather than offset are specified to enable some
* validation.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] level mip level of the image to set.
* @param[in] layer array layer of the image to set.
* @param[in] faceSlice cube map face or depth slice of the image to set.
* @param[in] src stdio stream pointer to the source.
* @param[in] srcSize size of the source image in bytes.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p src is NULL.
* @exception KTX_INVALID_VALUE @p srcSize != the expected image size for the
* specified level, layer & faceSlice.
* @exception KTX_INVALID_OPERATION
* No storage was allocated when the texture was
* created.
*/
KTX_error_code
ktxTexture1_SetImageFromStdioStream(ktxTexture1* This, ktx_uint32_t level,
ktx_uint32_t layer, ktx_uint32_t faceSlice,
FILE* src, ktx_size_t srcSize)
{
ktxStream srcstr;
KTX_error_code result;
result = ktxFileStream_construct(&srcstr, src, KTX_FALSE);
if (result != KTX_SUCCESS)
return result;
result = ktxTexture1_setImageFromStream(This, level, layer, faceSlice,
&srcstr, srcSize);
ktxFileStream_destruct(&srcstr);
return result;
}
/**
* @memberof ktxTexture1
* @~English
* @brief Set image for level, layer, faceSlice from an image in memory.
*
* Uncompressed images in memory are expected to have their rows tightly packed
* as is the norm for most image file formats. The copied image is padded as
* necessary to achieve the KTX-specified row alignment. No padding is done if
* the ktxTexture's @c isCompressed field is @c KTX_TRUE.
*
* Level, layer, faceSlice rather than offset are specified to enable some
* validation.
*
* @warning Do not use @c memcpy for this as it will not pad when necessary.
*
* @note The caller is responsible for freeing the original image memory
* referred to by @p src.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] level mip level of the image to set.
* @param[in] layer array layer of the image to set.
* @param[in] faceSlice cube map face or depth slice of the image to set.
* @param[in] src pointer to the image source in memory.
* @param[in] srcSize size of the source image in bytes.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p src is NULL.
* @exception KTX_INVALID_VALUE @p srcSize != the expected image size for the
* specified level, layer & faceSlice.
* @exception KTX_INVALID_OPERATION
* No storage was allocated when the texture was
* created.
*/
KTX_error_code
ktxTexture1_SetImageFromMemory(ktxTexture1* This, ktx_uint32_t level,
ktx_uint32_t layer, ktx_uint32_t faceSlice,
const ktx_uint8_t* src, ktx_size_t srcSize)
{
ktxStream srcstr;
KTX_error_code result;
result = ktxMemStream_construct_ro(&srcstr, src, srcSize);
if (result != KTX_SUCCESS)
return result;
result = ktxTexture1_setImageFromStream(This, level, layer, faceSlice,
&srcstr, srcSize);
ktxMemStream_destruct(&srcstr);
return result;
}
/**
* @memberof ktxTexture1
* @~English
* @brief Write a ktxTexture object to a ktxStream in KTX format.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] dststr destination ktxStream.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p dststr is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* Both kvDataHead and kvData are set in the
* ktxTexture
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture1_WriteToStream(ktxTexture1* This, ktxStream* dststr)
{
KTX_header header = { .identifier = KTX_IDENTIFIER_REF };
KTX_error_code result = KTX_SUCCESS;
ktx_uint8_t* pKvd = 0;
ktx_uint32_t level, levelOffset;
if (!dststr) {
return KTX_INVALID_VALUE;
}
if (This->pData == NULL)
return KTX_INVALID_OPERATION;
if (This->kvDataHead && This->kvData)
return KTX_INVALID_OPERATION;
//endianess int.. if this comes out reversed, all of the other ints will too.
header.endianness = KTX_ENDIAN_REF;
header.glInternalformat = This->glInternalformat;
header.glFormat = This->glFormat;
header.glBaseInternalformat = This->glBaseInternalformat;
header.glType = This->glType;
header.glTypeSize = ktxTexture1_glTypeSize(This);
header.pixelWidth = This->baseWidth;
header.pixelHeight = This->numDimensions > 1 ? This->baseHeight : 0;
header.pixelDepth = This->numDimensions > 2 ? This->baseDepth : 0;
header.numberOfArrayElements = This->isArray ? This->numLayers : 0;
assert (This->isCubemap ? This->numFaces == 6 : This->numFaces == 1);
header.numberOfFaces = This->numFaces;
assert (This->generateMipmaps ? This->numLevels == 1 : This->numLevels >= 1);
header.numberOfMipLevels = This->generateMipmaps ? 0 : This->numLevels;
if (This->kvDataHead != NULL) {
ktxHashList_Serialize(&This->kvDataHead,
&header.bytesOfKeyValueData, &pKvd);
} else if (This->kvData) {
pKvd = This->kvData;
header.bytesOfKeyValueData = This->kvDataLen;
} else {
header.bytesOfKeyValueData = 0;
}
//write header
result = dststr->write(dststr, &header, sizeof(KTX_header), 1);
if (result != KTX_SUCCESS)
return result;
//write keyValueData
if (header.bytesOfKeyValueData != 0) {
assert(pKvd != NULL);
result = dststr->write(dststr, pKvd, 1, header.bytesOfKeyValueData);
if (This->kvDataHead != NULL)
free(pKvd);
if (result != KTX_SUCCESS)
return result;
}
/* Write the image data */
for (level = 0, levelOffset=0; level < This->numLevels; ++level)
{
ktx_uint32_t faceLodSize, layer, levelDepth, numImages;
ktx_size_t imageSize;
faceLodSize = (ktx_uint32_t)ktxTexture_doCalcFaceLodSize(ktxTexture(This),
level,
KTX_FORMAT_VERSION_ONE);
imageSize = ktxTexture_GetImageSize(ktxTexture(This), level);
levelDepth = MAX(1, This->baseDepth >> level);
if (This->isCubemap && !This->isArray)
numImages = This->numFaces;
else
numImages = This->isCubemap ? This->numFaces : levelDepth;
result = dststr->write(dststr, &faceLodSize, sizeof(faceLodSize), 1);
if (result != KTX_SUCCESS)
goto cleanup;
for (layer = 0; layer < This->numLayers; layer++) {
ktx_uint32_t faceSlice;
for (faceSlice = 0; faceSlice < numImages; faceSlice++) {
result = dststr->write(dststr, This->pData + levelOffset,
imageSize, 1);
levelOffset += (ktx_uint32_t)imageSize;
}
}
}
cleanup:
return result;
}
/**
* @memberof ktxTexture1
* @~English
* @brief Write a ktxTexture object to a stdio stream in KTX format.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] dstsstr destination stdio stream.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p dstsstr is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* Both kvDataHead and kvData are set in the
* ktxTexture
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture1_WriteToStdioStream(ktxTexture1* This, FILE* dstsstr)
{
ktxStream stream;
KTX_error_code result = KTX_SUCCESS;
if (!This)
return KTX_INVALID_VALUE;
result = ktxFileStream_construct(&stream, dstsstr, KTX_FALSE);
if (result != KTX_SUCCESS)
return result;
return ktxTexture1_WriteToStream(This, &stream);
}
/**
* @memberof ktxTexture1
* @~English
* @brief Write a ktxTexture object to a named file in KTX format.
*
* The file name must be encoded in utf-8. On Windows convert unicode names
* to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] dstname destination file name.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p dstname is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* Both kvDataHead and kvData are set in the
* ktxTexture
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture1_WriteToNamedFile(ktxTexture1* This, const char* const dstname)
{
KTX_error_code result;
FILE* dst;
if (!This)
return KTX_INVALID_VALUE;
dst = ktxFOpenUTF8(dstname, "wb");
if (dst) {
result = ktxTexture1_WriteToStdioStream(This, dst);
fclose(dst);
} else
result = KTX_FILE_OPEN_FAILED;
return result;
}
/**
* @memberof ktxTexture1
* @~English
* @brief Write a ktxTexture object to block of memory in KTX format.
*
* Memory is allocated by the function and the caller is responsible for
* freeing it.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in,out] ppDstBytes pointer to location to write the address of
* the destination memory. The Application is
* responsible for freeing this memory.
* @param[in,out] pSize pointer to location to write the size in bytes of
* the KTX data.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This, @p ppDstBytes or @p pSize is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* Both kvDataHead and kvData are set in the
* ktxTexture
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture1_WriteToMemory(ktxTexture1* This,
ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)
{
struct ktxStream dststr;
KTX_error_code result;
ktx_size_t strSize;
if (!This || !ppDstBytes || !pSize)
return KTX_INVALID_VALUE;
*ppDstBytes = NULL;
result = ktxMemStream_construct(&dststr, KTX_FALSE);
if (result != KTX_SUCCESS)
return result;
result = ktxTexture1_WriteToStream(This, &dststr);
if(result != KTX_SUCCESS)
{
ktxMemStream_destruct(&dststr);
return result;
}
ktxMemStream_getdata(&dststr, ppDstBytes);
dststr.getsize(&dststr, &strSize);
*pSize = (GLsizei)strSize;
/* This function does not free the memory pointed at by the
* value obtained from ktxMemStream_getdata() thanks to the
* KTX_FALSE passed to the constructor above.
*/
ktxMemStream_destruct(&dststr);
return KTX_SUCCESS;
}
ktx_uint32_t lcm4(uint32_t a);
KTX_error_code appendLibId(ktxHashList* head,
ktxHashListEntry* writerEntry);
/**
* @memberof ktxTexture1
* @~English
* @brief Write a ktxTexture object to a ktxStream in KTX 2 format.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] dststr destination ktxStream.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p dststr is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* The ktxTexture contains unknownY KTX- or ktx-
* prefixed metadata keys.
* @exception KTX_INVALID_OPERATION
* The length of the already set writerId metadata
* plus the library's version id exceeds the
* maximum allowed.
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture1_WriteKTX2ToStream(ktxTexture1* This, ktxStream* dststr)
{
KTX_header2 header = { .identifier = KTX2_IDENTIFIER_REF };
KTX_error_code result;
ktx_uint32_t kvdLen;
ktx_uint8_t* pKvd;
ktx_uint32_t initialLevelPadLen;
ktxLevelIndexEntry* levelIndex = NULL;
ktx_uint32_t levelIndexSize;
ktx_uint32_t offset;
ktx_uint32_t requiredLevelAlignment;
if (!dststr) {
return KTX_INVALID_VALUE;
}
if (This->pData == NULL)
return KTX_INVALID_OPERATION;
header.vkFormat
= vkGetFormatFromOpenGLInternalFormat(This->glInternalformat);
// The above function does not return any formats in the prohibited list.
if (header.vkFormat == VK_FORMAT_UNDEFINED) {
// XXX TODO. Handle ASTC HDR & 3D.
return KTX_UNSUPPORTED_TEXTURE_TYPE;
}
header.typeSize = ktxTexture1_glTypeSize(This);
header.pixelWidth = This->baseWidth;
header.pixelHeight = This->numDimensions > 1 ? This->baseHeight : 0;
header.pixelDepth = This->numDimensions > 2 ? This->baseDepth : 0;
header.layerCount = This->isArray ? This->numLayers : 0;
assert (This->isCubemap ? This->numFaces == 6 : This->numFaces == 1);
header.faceCount = This->numFaces;
assert (This->generateMipmaps? This->numLevels == 1 : This->numLevels >= 1);
header.levelCount = This->generateMipmaps ? 0 : This->numLevels;
levelIndexSize = sizeof(ktxLevelIndexEntry) * This->numLevels;
levelIndex = (ktxLevelIndexEntry*) malloc(levelIndexSize);
offset = sizeof(header) + levelIndexSize;
ktx_uint32_t* dfd = vk2dfd(header.vkFormat);
if (!dfd) {
result = KTX_UNSUPPORTED_TEXTURE_TYPE;
goto cleanup;
}
header.dataFormatDescriptor.byteOffset = offset;
header.dataFormatDescriptor.byteLength = *dfd;
offset += header.dataFormatDescriptor.byteLength;
ktxHashListEntry* pEntry;
// Check for invalid metadata.
for (pEntry = This->kvDataHead; pEntry != NULL; pEntry = ktxHashList_Next(pEntry)) {
unsigned int keyLen;
char* key;
ktxHashListEntry_GetKey(pEntry, &keyLen, &key);
if (strncasecmp(key, "KTX", 3) == 0) {
// N.B. Writer metadata is not legal in a KTX v1 file but we know
// we're going to write this out as a v2 file so we allow it so
// conversion apps can identify themselves.
if (strcmp(key, KTX_ORIENTATION_KEY) && strcmp(key, KTX_WRITER_KEY)) {
result = KTX_INVALID_OPERATION;
goto cleanup;
}
}
}
result = ktxHashList_FindEntry(&This->kvDataHead, KTX_ORIENTATION_KEY,
&pEntry);
// Rewrite the orientation value in the KTX2 form.
if (result == KTX_SUCCESS) {
unsigned int count;
char* orientation;
ktx_uint32_t orientationLen;
char newOrient[4] = {0, 0, 0, 0};
result = ktxHashListEntry_GetValue(pEntry,
&orientationLen, (void**)&orientation);
count = sscanf(orientation, "S=%c,T=%c,R=%c",
&newOrient[0],
&newOrient[1],
&newOrient[2]);
if (count < This->numDimensions) {
// There needs to be an entry for each dimension of the texture.
result = KTX_FILE_DATA_ERROR;
goto cleanup;
} else if (count > This->numDimensions) {
// KTX 1 is less strict than KTX 2 so there is a chance of having
// more dimensions than needed.
count = This->numDimensions;
newOrient[count] = '\0';
}
ktxHashList_DeleteEntry(&This->kvDataHead, pEntry);
ktxHashList_AddKVPair(&This->kvDataHead, KTX_ORIENTATION_KEY,
count+1, newOrient);
}
pEntry = NULL;
// See comment at valid metadata check at line 582.
result = ktxHashList_FindEntry(&This->kvDataHead, KTX_WRITER_KEY,
&pEntry);
result = appendLibId(&This->kvDataHead, pEntry);
if (result != KTX_SUCCESS)
goto cleanup;
ktxHashList_Sort(&This->kvDataHead); // KTX2 requires sorted metadata.
ktxHashList_Serialize(&This->kvDataHead, &kvdLen, &pKvd);
header.keyValueData.byteOffset = kvdLen != 0 ? offset : 0;
header.keyValueData.byteLength = kvdLen;
offset += kvdLen;
header.supercompressionGlobalData.byteOffset = 0;
header.supercompressionGlobalData.byteLength = 0;
requiredLevelAlignment
= lcm4(This->_protected->_formatSize.blockSizeInBits / 8);
initialLevelPadLen = _KTX_PADN_LEN(requiredLevelAlignment, offset);
offset += initialLevelPadLen;
for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--) {
ktx_size_t levelSize =
ktxTexture_calcLevelSize(ktxTexture(This), level,
KTX_FORMAT_VERSION_TWO);
levelIndex[level].uncompressedByteLength = levelSize;
levelIndex[level].byteLength = levelSize;
levelIndex[level].byteOffset = offset;
offset += _KTX_PADN(requiredLevelAlignment, levelSize);
}
// write header and indices
result = dststr->write(dststr, &header, sizeof(header), 1);
if (result != KTX_SUCCESS)
goto cleanup;
// write level index
result = dststr->write(dststr, levelIndex, levelIndexSize, 1);
if (result != KTX_SUCCESS)
goto cleanup;
// write data format descriptor
result = dststr->write(dststr, dfd, 1, *dfd);
// write keyValueData
if (kvdLen != 0) {
assert(pKvd != NULL);
result = dststr->write(dststr, pKvd, 1, kvdLen);
free(pKvd);
if (result != KTX_SUCCESS) {
goto cleanup;
}
}
char padding[32] = { 0 };
// write supercompressionGlobalData & sgdPadding
if (initialLevelPadLen) {
result = dststr->write(dststr, padding, 1, initialLevelPadLen);
if (result != KTX_SUCCESS) {
goto cleanup;
}
}
// Write the image data
for (ktx_int32_t level = This->numLevels - 1;
level >= 0 && result == KTX_SUCCESS; --level)
{
//ktx_uint64_t faceLodSize;
ktx_uint32_t layer, levelDepth, numImages;
ktx_uint32_t srcLevelOffset, srcOffset;
ktx_size_t imageSize, dstLevelSize = 0;
#define DUMP_IMAGE 0
#if defined(DEBUG) || DUMP_IMAGE
ktx_size_t pos;
#endif
imageSize = ktxTexture_calcImageSize(ktxTexture(This), level,
KTX_FORMAT_VERSION_TWO);
#if defined(DEBUG)
result = dststr->getpos(dststr, (ktx_off_t*)&pos);
// Could fail if stdout is a pipe
if (result == KTX_SUCCESS)
assert(pos == levelIndex[level].byteOffset);
else
assert(result == KTX_FILE_ISPIPE);
#endif
levelDepth = MAX(1, This->baseDepth >> level);
if (This->isCubemap && !This->isArray)
numImages = This->numFaces;
else
numImages = This->isCubemap ? This->numFaces : levelDepth;
ktx_uint32_t numRows = 0, rowBytes = 0, rowPadding = 0;
if (!This->isCompressed) {
ktxTexture_rowInfo(ktxTexture(This), level, &numRows, &rowBytes,
&rowPadding);
}
srcLevelOffset = (ktx_uint32_t)ktxTexture_calcLevelOffset(
ktxTexture(This),
level);
srcOffset = srcLevelOffset;
for (layer = 0; layer < This->numLayers; layer++) {
ktx_uint32_t faceSlice;
for (faceSlice = 0; faceSlice < numImages; faceSlice++) {
#if DUMP_IMAGE
dststr->getsize(dststr, &pos);
fprintf(stdout, "Writing level %d, layer %d, faceSlice %d to offset %#zx\n",
level, layer, faceSlice, pos);
#endif
if (rowPadding == 0) {
#if DUMP_IMAGE
if (!This->isCompressed)
for (uint32_t y = 0; y < (This->baseHeight >> level); y++) {
for (uint32_t x = 0; x < rowBytes; x++) {
fprintf(stdout, "%#x, ",
*(This->pData + srcOffset + y * rowBytes + x));
}
fprintf(stdout, "\n");
}
#endif
// Write entire image.
result = dststr->write(dststr, This->pData + srcOffset,
imageSize, 1);
dstLevelSize += imageSize;
} else {
/* Copy the rows individually, removing padding. */
ktx_uint32_t row;
ktx_uint8_t* src = This->pData + srcOffset;
ktx_uint32_t packedRowBytes = rowBytes - rowPadding;
for (row = 0; row < numRows; row++) {
ktx_uint32_t rowOffset = rowBytes * row;
#if DUMP_IMAGE
for (uint32_t i = 0; i < packedRowBytes; i++)
fprintf(stdout, "%#x, ", *(src + rowOffset + i));
#endif
result = dststr->write(dststr, src + rowOffset,
packedRowBytes, 1);
dstLevelSize += packedRowBytes;
}
}
#if DUMP_IMAGE
fprintf(stdout, "\n");
#endif
srcOffset += (ktx_uint32_t)imageSize;
}
}
if (result == KTX_SUCCESS && level != 0) {
uint32_t levelPadLen = _KTX_PADN_LEN(requiredLevelAlignment,
dstLevelSize);
if (levelPadLen)
result = dststr->write(dststr, padding, 1, levelPadLen);
}
}
cleanup:
free(dfd);
free(levelIndex);
return result;
}
/**
* @memberof ktxTexture1
* @~English
* @brief Write a ktxTexture object to a stdio stream in KTX2 format.
*
* Callers are strongly urged to include a KTXwriter item in the texture's metadata.
* It can be added by code, similar to the following, prior to calling this
* function.
* @code
* char writer[100];
* snprintf(writer, sizeof(writer), "%s version %s", appName, appVer);
* ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY,
* (ktx_uint32_t)strlen(writer) + 1,
* writer);
* @endcode
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] dstsstr destination stdio stream.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p dstsstr is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* The ktxTexture contains unknownY KTX- or ktx-
* prefixed metadata keys.
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture1_WriteKTX2ToStdioStream(ktxTexture1* This, FILE* dstsstr)
{
ktxStream stream;
KTX_error_code result = KTX_SUCCESS;
if (!This)
return KTX_INVALID_VALUE;
result = ktxFileStream_construct(&stream, dstsstr, KTX_FALSE);
if (result != KTX_SUCCESS)
return result;
return ktxTexture1_WriteKTX2ToStream(This, &stream);
}
/**
* @memberof ktxTexture1
* @~English
* @brief Write a ktxTexture object to a named file in KTX2 format.
*
* The file name must be encoded in utf-8. On Windows convert unicode names
* to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.
*
* Callers are strongly urged to include a KTXwriter item in the texture's
* metadata. It can be added by code, similar to the following, prior to
* calling this function.
* @code
* char writer[100];
* snprintf(writer, sizeof(writer), "%s version %s", appName, appVer);
* ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY,
* (ktx_uint32_t)strlen(writer) + 1,
* writer);
* @endcode
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] dstname destination file name.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p dstname is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* The ktxTexture contains unknownY KTX- or ktx-
* prefixed metadata keys.
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture1_WriteKTX2ToNamedFile(ktxTexture1* This, const char* const dstname)
{
KTX_error_code result;
FILE* dst;
if (!This)
return KTX_INVALID_VALUE;
dst = ktxFOpenUTF8(dstname, "wb");
if (dst) {
result = ktxTexture1_WriteKTX2ToStdioStream(This, dst);
fclose(dst);
} else
result = KTX_FILE_OPEN_FAILED;
return result;
}
/**
* @memberof ktxTexture1
* @~English
* @brief Write a ktxTexture object to block of memory in KTX2 format.
*
* Memory is allocated by the function and the caller is responsible for
* freeing it.
*
* Callers are strongly urged to include a KTXwriter item in the texture's metadata.
* It can be added by code, similar to the following, prior to calling this
* function.
* @code
* char writer[100];
* snprintf(writer, sizeof(writer), "%s version %s", appName, appVer);
* ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY,
* (ktx_uint32_t)strlen(writer) + 1,
* writer);
* @endcode
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in,out] ppDstBytes pointer to location to write the address of
* the destination memory. The Application is
* responsible for freeing this memory.
* @param[in,out] pSize pointer to location to write the size in bytes of
* the KTX data.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This, @p ppDstBytes or @p pSize is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* The ktxTexture contains unknownY KTX- or ktx-
* prefixed metadata keys.
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture1_WriteKTX2ToMemory(ktxTexture1* This,
ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)
{
struct ktxStream dststr;
KTX_error_code result;
ktx_size_t strSize;
if (!This || !ppDstBytes || !pSize)
return KTX_INVALID_VALUE;
*ppDstBytes = NULL;
result = ktxMemStream_construct(&dststr, KTX_FALSE);
if (result != KTX_SUCCESS)
return result;
result = ktxTexture1_WriteKTX2ToStream(This, &dststr);
if(result != KTX_SUCCESS)
{
ktxMemStream_destruct(&dststr);
return result;
}
ktxMemStream_getdata(&dststr, ppDstBytes);
dststr.getsize(&dststr, &strSize);
*pSize = (GLsizei)strSize;
/* This function does not free the memory pointed at by the
* value obtained from ktxMemStream_getdata() thanks to the
* KTX_FALSE passed to the constructor above.
*/
ktxMemStream_destruct(&dststr);
return KTX_SUCCESS;
}
/** @} */
+990
View File
@@ -0,0 +1,990 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/**
* @internal
* @file
* @~English
*
* @brief Functions for creating KTX-format files from a set of images.
*
* @author Mark Callow, HI Corporation
*/
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__GNUC__)
#include <strings.h> // For strncasecmp on GNU/Linux
#endif
#include <zstd.h>
#include <zstd_errors.h>
#include <KHR/khr_df.h>
#include "ktx.h"
#include "ktxint.h"
#include "filestream.h"
#include "memstream.h"
#include "texture2.h"
#include "dfdutils/dfd.h"
#include "vkformat_enum.h"
#include "vk_format.h"
#include "version.h"
#if defined(_MSC_VER)
#define strncasecmp _strnicmp
#endif
/**
* @defgroup writer Writer
* @brief Write KTX-formatted data.
* @{
*/
#if defined(_WIN32) || defined(linux) || defined(__linux) || defined(__linux__) || defined(__EMSCRIPTEN__)
/** @internal
* @~English
* @brief strnstr for Windows, Linux and Emscripten.
*
* strnstr is available in <apple>OS and BSD distributions. To use in Linux
* requires linking an additional library, libbsd. It is simpler to use ours.
*
* @param[in] haystack pointer to string to search.
* @param[in] needle pointer to string to search for.
* @param[in] len length of @p haystack string. Also used as limit to
* length of @p needle string.
*
* @return @p haystack, if @p needle is an empty string otherwise NULL, if
* @p needle does not occur in @p haystack, or a pointer to the
* first character of the first occurrence of @p needle.
*/
static char*
strnstr(const char *haystack, const char *needle, size_t len)
{
size_t i;
size_t needleLen;
const char* needleEnd;
// strnlen is not part of the C standard and does not compile on some platforms,
// use case is covered by memchr.
needleEnd = (char *)memchr(needle, 0, len);
if (needleEnd == needle)
return (char *)haystack;
needleLen = len;
if (needleEnd != NULL)
needleLen = needleEnd - needle;
for (i = 0; i <= len - needleLen; i++)
{
if (haystack[0] == needle[0]
&& strncmp(haystack, needle, needleLen) == 0)
return (char *)haystack;
haystack++;
}
return NULL;
}
#endif
/** @internal
* @~English
* @brief Append the library's id to the KTXwriter value.
*
* @param[in] head pointer to the head of the hash list.
* @param[in] writerEntry pointer to an existing KTXwriter entry.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_OUT_OF_MEMORY not enough memory for temporary strings.
* @exception KTX_INVALID_OPERATION
* the length of the value of writerEntry and the
* lib id being added is greater than the
* maximum allowed.
*/
KTX_error_code
appendLibId(ktxHashList* head, ktxHashListEntry* writerEntry)
{
KTX_error_code result;
const char* id;
const char* libVer;
const char libIdIntro[] = " / libktx ";
size_t idLen, libIdLen;
if (writerEntry) {
ktx_uint32_t len;
result = ktxHashListEntry_GetValue(writerEntry, &len, (void**)&id);
idLen = len;
} else {
id = "Unidentified app";
idLen = 17;
}
// strnstr needed because KTXwriter values may not be NUL terminated.
#if defined(EMPTY_LIBVER_WITH_UNIDENTIFIED_APP)
// May be needed for patching some CTS files without changing their KTXwriter
// metadata. Keep in case useful again.
if (strnstr(id, "Unidentified app", idLen) != NULL) {
libVer = "";
} else
#endif
if (strnstr(id, "__default__", idLen) != NULL) {
libVer = STR(LIBKTX_DEFAULT_VERSION);
} else {
libVer = STR(LIBKTX_VERSION);
}
// sizeof(libIdIntro) includes space for its terminating NUL which we will
// overwrite so no need for +1 after strlen.
libIdLen = sizeof(libIdIntro) + (ktx_uint32_t)strlen(libVer);
char* libId = malloc(libIdLen);
if (!libId)
return KTX_OUT_OF_MEMORY;
strncpy(libId, libIdIntro, libIdLen);
strncpy(&libId[sizeof(libIdIntro)-1], libVer,
libIdLen-(sizeof(libIdIntro)-1));
char* fullId = NULL;
if (strnstr(id, libId, idLen) != NULL) {
// This lib id is already in the writer value.
result = KTX_SUCCESS;
goto cleanup;
}
const char* libVerPos = strnstr(id, libIdIntro, idLen);
if (libVerPos != NULL) {
// There is a libktx version but not the current version.
idLen = libVerPos - id;
} else if (id[idLen-1] == '\0') {
idLen--;
}
size_t fullIdLen = idLen + strlen(libId) + 1;
if (fullIdLen > UINT_MAX) {
result = KTX_INVALID_OPERATION;
goto cleanup;
}
fullId = malloc(fullIdLen);
if (!fullId) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
strncpy(fullId, id, idLen);
strncpy(&fullId[idLen], libId, libIdLen);
assert(fullId[fullIdLen-1] == '\0');
ktxHashList_DeleteEntry(head, writerEntry);
result = ktxHashList_AddKVPair(head, KTX_WRITER_KEY,
(ktx_uint32_t)fullIdLen, fullId);
cleanup:
free(libId);
free(fullId);
return result;
}
/**
* @memberof ktxTexture2 @private
* @~English
* @brief Set image for level, layer, faceSlice from a ktxStream source.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] level mip level of the image to set.
* @param[in] layer array layer of the image to set.
* @param[in] faceSlice cube map face or depth slice of the image to set or
* KTX_FACESLICE_WHOLE_LEVEL to set the entire level.
* @param[in] src ktxStream pointer to the source.
* @param[in] srcSize size of the source image in bytes.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p src is NULL.
* @exception KTX_INVALID_VALUE @p srcSize != the expected image size for the
* specified level, layer & faceSlice.
* @exception KTX_INVALID_OPERATION
* No storage was allocated when the texture was
* created.
*/
KTX_error_code
ktxTexture2_setImageFromStream(ktxTexture2* This, ktx_uint32_t level,
ktx_uint32_t layer, ktx_uint32_t faceSlice,
ktxStream* src, ktx_size_t srcSize)
{
ktx_size_t imageByteLength;
ktx_size_t imageByteOffset;
ktx_error_code_e result;
if (!This || !src)
return KTX_INVALID_VALUE;
if (!This->pData)
return KTX_INVALID_OPERATION;
if (faceSlice == KTX_FACESLICE_WHOLE_LEVEL) {
result = ktxTexture_GetImageOffset(ktxTexture(This), level, layer, 0, &imageByteOffset);
if (result != KTX_SUCCESS) {
return result;
}
imageByteLength = ktxTexture_calcLevelSize(ktxTexture(This), level, KTX_FORMAT_VERSION_TWO);
} else {
result = ktxTexture_GetImageOffset(ktxTexture(This), level, layer, faceSlice, &imageByteOffset);
if (result != KTX_SUCCESS) {
return result;
}
imageByteLength = ktxTexture_GetImageSize(ktxTexture(This), level);
}
if (srcSize != imageByteLength)
return KTX_INVALID_OPERATION;
// The above will catch a flagrantly invalid srcSize. This is an
// additional check of the internal calculations.
assert (imageByteOffset + srcSize <= This->dataSize);
/* Can copy whole image at once */
src->read(src, This->pData + imageByteOffset, srcSize);
return KTX_SUCCESS;
}
/**
* @memberof ktxTexture2
* @~English
* @brief Set image for level, layer, faceSlice from a stdio stream source.
*
* Uncompressed images read from the stream are expected to have their rows
* tightly packed as is the norm for most image file formats. KTX 2 also requires
* tight packing this function does not add any padding.
*
* Level, layer, faceSlice rather than offset are specified to enable some
* validation.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] level mip level of the image to set.
* @param[in] layer array layer of the image to set.
* @param[in] faceSlice cube map face or depth slice of the image to set or
* KTX_FACESLICE_WHOLE_LEVEL to set the entire level.
* @param[in] src stdio stream pointer to the source.
* @param[in] srcSize size of the source image in bytes.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p src is NULL.
* @exception KTX_INVALID_VALUE @p srcSize != the expected image size for the
* specified level, layer & faceSlice.
* @exception KTX_INVALID_OPERATION
* No storage was allocated when the texture was
* created.
*/
KTX_error_code
ktxTexture2_SetImageFromStdioStream(ktxTexture2* This, ktx_uint32_t level,
ktx_uint32_t layer, ktx_uint32_t faceSlice,
FILE* src, ktx_size_t srcSize)
{
ktxStream srcstr;
KTX_error_code result;
result = ktxFileStream_construct(&srcstr, src, KTX_FALSE);
if (result != KTX_SUCCESS)
return result;
result = ktxTexture2_setImageFromStream(This, level, layer, faceSlice,
&srcstr, srcSize);
ktxFileStream_destruct(&srcstr);
return result;
}
/**
* @memberof ktxTexture2
* @~English
* @brief Set image for level, layer, faceSlice from an image in memory.
*
* Uncompressed images in memory are expected to have their rows tightly packed
* as is the norm for most image file formats. KTX 2 also requires
* tight packing this function does not add any padding.
*
* Level, layer, faceSlice rather than offset are specified to enable some
* validation.
*
* @note The caller is responsible for freeing the original image memory
* referred to by @p src.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] level mip level of the image to set.
* @param[in] layer array layer of the image to set.
* @param[in] faceSlice cube map face or depth slice of the image to set or
* KTX_FACESLICE_WHOLE_LEVEL to set the entire level.
* @param[in] src pointer to the image source in memory.
* @param[in] srcSize size of the source image in bytes.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p src is NULL.
* @exception KTX_INVALID_VALUE @p srcSize != the expected image size for the
* specified level, layer & faceSlice.
* @exception KTX_INVALID_OPERATION
* No storage was allocated when the texture was
* created.
*/
KTX_error_code
ktxTexture2_SetImageFromMemory(ktxTexture2* This, ktx_uint32_t level,
ktx_uint32_t layer, ktx_uint32_t faceSlice,
const ktx_uint8_t* src, ktx_size_t srcSize)
{
ktxStream srcstr;
KTX_error_code result;
result = ktxMemStream_construct_ro(&srcstr, src, srcSize);
if (result != KTX_SUCCESS)
return result;
result = ktxTexture2_setImageFromStream(This, level, layer, faceSlice,
&srcstr, srcSize);
ktxMemStream_destruct(&srcstr);
return result;
}
#if defined(TestNoMetadata)
// Only so texturetests can test loading of files without any metadata.
ktx_bool_t __disableWriterMetadata__ = KTX_FALSE;
#endif
/**
* @memberof ktxTexture2
* @~English
* @brief Write a ktxTexture object to a ktxStream in KTX format.
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] dststr destination ktxStream.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p dststr is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* Both kvDataHead and kvData are set in the
* ktxTexture
* @exception KTX_INVALID_OPERATION
* The length of the already set writerId metadata
* plus the library's version id exceeds the
* maximum allowed.
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture2_WriteToStream(ktxTexture2* This, ktxStream* dststr)
{
DECLARE_PRIVATE(ktxTexture2);
KTX_header2 header = { .identifier = KTX2_IDENTIFIER_REF };
KTX_error_code result;
ktx_uint32_t kvdLen;
ktx_uint8_t* pKvd;
ktx_uint32_t align8PadLen = 0;
ktx_uint64_t sgdLen;
ktx_uint32_t initialLevelPadLen;
ktx_uint32_t levelIndexSize;
ktx_uint64_t baseOffset;
if (!dststr) {
return KTX_INVALID_VALUE;
}
if (This->pData == NULL)
return KTX_INVALID_OPERATION;
header.vkFormat = This->vkFormat;
header.typeSize = This->_protected->_typeSize;
header.pixelWidth = This->baseWidth;
header.pixelHeight = This->numDimensions > 1 ? This->baseHeight : 0;
header.pixelDepth = This->numDimensions > 2 ? This->baseDepth : 0;
header.layerCount = This->isArray ? This->numLayers : 0;
assert (This->isCubemap ? This->numFaces == 6 : This->numFaces == 1);
header.faceCount = This->numFaces;
assert (This->generateMipmaps? This->numLevels == 1 : This->numLevels >= 1);
header.levelCount = This->generateMipmaps ? 0 : This->numLevels;
header.supercompressionScheme = This->supercompressionScheme;
levelIndexSize = sizeof(ktxLevelIndexEntry) * This->numLevels;
baseOffset = sizeof(header) + levelIndexSize;
header.dataFormatDescriptor.byteOffset = (uint32_t)baseOffset;
header.dataFormatDescriptor.byteLength = *This->pDfd;
baseOffset += header.dataFormatDescriptor.byteLength;
ktxHashListEntry* pEntry;
// Check for invalid metadata.
for (pEntry = This->kvDataHead; pEntry != NULL; pEntry = ktxHashList_Next(pEntry)) {
unsigned int keyLen;
char* key;
ktxHashListEntry_GetKey(pEntry, &keyLen, &key);
if (strncasecmp(key, "KTX", 3) == 0) {
ktx_uint32_t i;
const char* knownKeys[] = {
"KTXcubemapIncomplete",
"KTXorientation",
"KTXglFormat",
"KTXdxgiFormat__",
"KTXmetalPixelFormat",
"KTXswizzle",
"KTXwriter",
"KTXwriterScParams",
"KTXastcDecodeMode",
"KTXanimData"
};
if (strncmp(key, "ktx", 3) == 0)
return KTX_INVALID_OPERATION;
// Check for unrecognized KTX keys.
for (i = 0; i < sizeof(knownKeys)/sizeof(char*); i++) {
if (strcmp(key, knownKeys[i]) == 0)
break;
}
if (i == sizeof(knownKeys)/sizeof(char*))
return KTX_INVALID_OPERATION;
}
}
#if defined(TestNoMetadata)
if (!__disableWriterMetadata__) {
#endif
pEntry = NULL;
result = ktxHashList_FindEntry(&This->kvDataHead, KTX_WRITER_KEY,
&pEntry);
result = appendLibId(&This->kvDataHead, pEntry);
if (result != KTX_SUCCESS)
return result;
#if defined(TestNoMetadata)
}
#endif
ktxHashList_Sort(&This->kvDataHead); // KTX2 requires sorted metadata.
ktxHashList_Serialize(&This->kvDataHead, &kvdLen, &pKvd);
header.keyValueData.byteOffset = kvdLen != 0 ? (uint32_t)baseOffset : 0;
header.keyValueData.byteLength = kvdLen;
baseOffset += kvdLen;
sgdLen = private->_sgdByteLength;
if (sgdLen) {
align8PadLen = _KTX_PAD8_LEN(baseOffset);
baseOffset += align8PadLen;
}
header.supercompressionGlobalData.byteOffset = sgdLen != 0 ? baseOffset : 0;
header.supercompressionGlobalData.byteLength = sgdLen;
baseOffset += sgdLen;
initialLevelPadLen = _KTX_PADN_LEN(This->_private->_requiredLevelAlignment,
baseOffset);
baseOffset += initialLevelPadLen;
// write header and indices
result = dststr->write(dststr, &header, sizeof(header), 1);
if (result != KTX_SUCCESS)
return result;
// Create a copy of the level index with file-adjusted offsets and write it.
ktxLevelIndexEntry* levelIndex
= (ktxLevelIndexEntry*)malloc(levelIndexSize);
if (!levelIndex)
return KTX_OUT_OF_MEMORY;
for (ktx_uint32_t level = 0; level < This->numLevels; level++) {
levelIndex[level].byteLength = private->_levelIndex[level].byteLength;
levelIndex[level].uncompressedByteLength
= private->_levelIndex[level].uncompressedByteLength;
levelIndex[level].byteOffset = private->_levelIndex[level].byteOffset;
levelIndex[level].byteOffset += baseOffset;
}
result = dststr->write(dststr, levelIndex, levelIndexSize, 1);
free(levelIndex);
if (result != KTX_SUCCESS)
return result;
// write data format descriptor
result = dststr->write(dststr, This->pDfd, 1, *This->pDfd);
// write keyValueData
if (kvdLen != 0) {
assert(pKvd != NULL);
result = dststr->write(dststr, pKvd, 1, kvdLen);
free(pKvd);
if (result != KTX_SUCCESS) {
return result;
}
}
char padding[32] = { 0 };
// write supercompressionGlobalData & sgdPadding
if (private->_sgdByteLength != 0) {
if (align8PadLen) {
result = dststr->write(dststr, padding, 1, align8PadLen);
if (result != KTX_SUCCESS) {
return result;
}
}
result = dststr->write(dststr, private->_supercompressionGlobalData,
1, private->_sgdByteLength);
if (result != KTX_SUCCESS) {
return result;
}
}
if (initialLevelPadLen) {
result = dststr->write(dststr, padding, 1, initialLevelPadLen);
if (result != KTX_SUCCESS) {
return result;
}
}
// write the image data
for (ktx_int32_t level = This->numLevels-1; level >= 0 && result == KTX_SUCCESS; --level)
{
ktx_uint64_t srcLevelOffset, levelSize;
#define DUMP_IMAGE 0
#if defined(DEBUG) || DUMP_IMAGE
ktx_size_t pos;
#endif
#if defined(DEBUG)
result = dststr->getpos(dststr, (ktx_off_t*)&pos);
// Could fail if stdout is a pipe
if (result == KTX_SUCCESS)
assert(pos == private->_levelIndex[level].byteOffset + baseOffset);
else
assert(result == KTX_FILE_ISPIPE);
#endif
srcLevelOffset = ktxTexture2_levelDataOffset(This, level);
levelSize = private->_levelIndex[level].byteLength;
#if DUMP_IMAGE
if (!This->isCompressed) {
for (layer = 0; layer < This->numLayers; layer++) {
ktx_uint32_t faceSlice;
for (faceSlice = 0; faceSlice < numImages; faceSlice++) {
dststr->getsize(dststr, &pos);
fprintf(stdout, "Writing level %d, layer %d, faceSlice %d to baseOffset %#zx\n",
level, layer, faceSlice, pos);
for (uint32_t y = 0; y < (This->baseHeight >> level); y++) {
for (uint32_t x = 0; x < rowBytes; x++) {
fprintf(stdout, "%#x, ",
*(This->pData + srcOffset + y * rowBytes + x));
}
fprintf(stdout, "\n");
}
}
}
}
fprintf(stdout, "\n");
#endif
// Write entire level.
result = dststr->write(dststr, This->pData + srcLevelOffset,
levelSize, 1);
if (result == KTX_SUCCESS && level > 0) { // No padding at end.
ktx_uint32_t levelPadLen
= _KTX_PADN_LEN(This->_private->_requiredLevelAlignment,
levelSize);
if (levelPadLen != 0)
result = dststr->write(dststr, padding, 1, levelPadLen);
}
}
return result;
}
/**
* @memberof ktxTexture2
* @~English
* @brief Write a ktxTexture object to a stdio stream in KTX format.
*
* Callers are strongly urged to include a KTXwriter item in the texture's
* metadata. It can be added by code, similar to the following, prior to
* calling this function.
* @code
* char writer[100];
* snprintf(writer, sizeof(writer), "%s version %s", appName, appVer);
* ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY,
* (ktx_uint32_t)strlen(writer) + 1,
* writer);
* @endcode
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] dstsstr destination stdio stream.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p dstsstr is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* Both kvDataHead and kvData are set in the
* ktxTexture
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture2_WriteToStdioStream(ktxTexture2* This, FILE* dstsstr)
{
ktxStream stream;
KTX_error_code result = KTX_SUCCESS;
if (!This)
return KTX_INVALID_VALUE;
result = ktxFileStream_construct(&stream, dstsstr, KTX_FALSE);
if (result != KTX_SUCCESS)
return result;
return ktxTexture2_WriteToStream(This, &stream);
}
/**
* @memberof ktxTexture2
* @~English
* @brief Write a ktxTexture object to a named file in KTX format.
*
* The file name must be encoded in utf-8. On Windows convert unicode names
* to utf-8 with @c WideCharToMultiByte(CP_UTF8, ...) before calling.
*
* Callers are strongly urged to include a KTXwriter item in the texture's
* metadata. It can be added by code, similar to the following, prior to
* calling this function.
* @code
* char writer[100];
* snprintf(writer, sizeof(writer), "%s version %s", appName, appVer);
* ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY,
* (ktx_uint32_t)strlen(writer) + 1,
* writer);
* @endcode
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in] dstname destination file name.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This or @p dstname is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* Both kvDataHead and kvData are set in the
* ktxTexture
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture2_WriteToNamedFile(ktxTexture2* This, const char* const dstname)
{
KTX_error_code result;
FILE* dst;
if (!This)
return KTX_INVALID_VALUE;
dst = ktxFOpenUTF8(dstname, "wb");
if (dst) {
result = ktxTexture2_WriteToStdioStream(This, dst);
fclose(dst);
} else
result = KTX_FILE_OPEN_FAILED;
return result;
}
/**
* @memberof ktxTexture2
* @~English
* @brief Write a ktxTexture object to block of memory in KTX format.
*
* Memory is allocated by the function and the caller is responsible for
* freeing it.
*
* Callers are strongly urged to include a KTXwriter item in the texture's
* metadata. It can be added by code, similar to the following, prior to
* calling this function.
* @code
* char writer[100];
* snprintf(writer, sizeof(writer), "%s version %s", appName, appVer);
* ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY,
* (ktx_uint32_t)strlen(writer) + 1,
* writer);
* @endcode
*
* @param[in] This pointer to the target ktxTexture object.
* @param[in,out] ppDstBytes pointer to location to write the address of
* the destination memory. The Application is
* responsible for freeing this memory.
* @param[in,out] pSize pointer to location to write the size in bytes of
* the KTX data.
*
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
*
* @exception KTX_INVALID_VALUE @p This, @p ppDstBytes or @p pSize is NULL.
* @exception KTX_INVALID_OPERATION
* The ktxTexture does not contain any image data.
* @exception KTX_INVALID_OPERATION
* Both kvDataHead and kvData are set in the
* ktxTexture
* @exception KTX_FILE_OVERFLOW The file exceeded the maximum size supported by
* the system.
* @exception KTX_FILE_WRITE_ERROR
* An error occurred while writing the file.
*/
KTX_error_code
ktxTexture2_WriteToMemory(ktxTexture2* This,
ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)
{
struct ktxStream dststr;
KTX_error_code result;
ktx_size_t strSize;
if (!This || !ppDstBytes || !pSize)
return KTX_INVALID_VALUE;
*ppDstBytes = NULL;
result = ktxMemStream_construct(&dststr, KTX_FALSE);
if (result != KTX_SUCCESS)
return result;
result = ktxTexture2_WriteToStream(This, &dststr);
if(result != KTX_SUCCESS)
{
ktxMemStream_destruct(&dststr);
return result;
}
ktxMemStream_getdata(&dststr, ppDstBytes);
dststr.getsize(&dststr, &strSize);
*pSize = (GLsizei)strSize;
/* This function does not free the memory pointed at by the
* value obtained from ktxMemStream_getdata() thanks to the
* KTX_FALSE passed to the constructor above.
*/
ktxMemStream_destruct(&dststr);
return KTX_SUCCESS;
}
/**
* @memberof ktxTexture2
* @~English
* @brief Deflate the data in a ktxTexture2 object using Zstandard.
*
* The texture's levelIndex, dataSize, DFD, data pointer, and supercompressionScheme will
* all be updated after successful deflation to reflect the deflated data.
*
* @param[in] This pointer to the ktxTexture2 object of interest.
* @param[in] compressionLevel set speed vs compression ratio trade-off. Values
* between 1 and 22 are accepted. The lower the level the faster. Values
* above 20 should be used with caution as they require more memory.
*/
KTX_error_code
ktxTexture2_DeflateZstd(ktxTexture2* This, ktx_uint32_t compressionLevel)
{
ktx_uint32_t levelIndexByteLength =
This->numLevels * sizeof(ktxLevelIndexEntry);
ktx_uint8_t* workBuf;
ktx_uint8_t* cmpData;
ktx_size_t dstRemainingByteLength = 0;
ktx_size_t byteLengthCmp = 0;
ktx_size_t levelOffset = 0;
ktxLevelIndexEntry* cindex = This->_private->_levelIndex;
ktxLevelIndexEntry* nindex;
ktx_uint8_t* pCmpDst;
ktx_error_code_e result;
ZSTD_CCtx* cctx = ZSTD_createCCtx();
if (cctx == NULL)
return KTX_OUT_OF_MEMORY;
if (This->supercompressionScheme != KTX_SS_NONE)
return KTX_INVALID_OPERATION;
// On rare occasions the deflated data can be a few bytes larger than
// the source data. Calculating the dst buffer size using
// ZSTD_compressBound provides a suitable size plus compression is said
// to run faster when the dst buffer is >= compressBound.
for (int32_t level = This->numLevels - 1; level >= 0; level--) {
dstRemainingByteLength += ZSTD_compressBound(cindex[level].byteLength);
}
workBuf = malloc(dstRemainingByteLength + levelIndexByteLength);
if (workBuf == NULL) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
nindex = (ktxLevelIndexEntry*)workBuf;
pCmpDst = &workBuf[levelIndexByteLength];
for (int32_t level = This->numLevels - 1; level >= 0; level--) {
size_t levelByteLengthCmp =
ZSTD_compressCCtx(cctx, pCmpDst + levelOffset,
dstRemainingByteLength,
&This->pData[cindex[level].byteOffset],
cindex[level].byteLength,
compressionLevel);
if (ZSTD_isError(levelByteLengthCmp)) {
free(workBuf);
ZSTD_ErrorCode error = ZSTD_getErrorCode(levelByteLengthCmp);
switch(error) {
case ZSTD_error_parameter_outOfBound:
result = KTX_INVALID_VALUE;
goto cleanup;
case ZSTD_error_dstSize_tooSmall:
#ifdef DEBUG
assert(false && "Deflate dstSize too small.");
#else
result = KTX_OUT_OF_MEMORY;
goto cleanup;
#endif
case ZSTD_error_workSpace_tooSmall:
#ifdef DEBUG
assert(false && "Deflate workspace too small.");
#else
result = KTX_OUT_OF_MEMORY;
goto cleanup;
#endif
case ZSTD_error_memory_allocation:
result = KTX_OUT_OF_MEMORY;
goto cleanup;
default:
// The remaining errors look like they should only
// occur during decompression but just in case.
#ifdef DEBUG
assert(true);
#else
result = KTX_INVALID_OPERATION;
goto cleanup;
#endif
}
}
nindex[level].byteOffset = levelOffset;
nindex[level].uncompressedByteLength = cindex[level].byteLength;
nindex[level].byteLength = levelByteLengthCmp;
byteLengthCmp += levelByteLengthCmp;
levelOffset += levelByteLengthCmp;
dstRemainingByteLength -= levelByteLengthCmp;
}
ZSTD_freeCCtx(cctx);
// Move the compressed data into a correctly sized buffer.
cmpData = malloc(byteLengthCmp);
if (cmpData == NULL) {
free(workBuf);
return KTX_OUT_OF_MEMORY;
}
// Now modify the texture.
memcpy(cmpData, pCmpDst, byteLengthCmp); // Copy data to sized buffer.
memcpy(cindex, nindex, levelIndexByteLength); // Update level index
free(workBuf);
free(This->pData);
This->pData = cmpData;
This->dataSize = byteLengthCmp;
This->supercompressionScheme = KTX_SS_ZSTD;
This->_private->_requiredLevelAlignment = 1;
return KTX_SUCCESS;
cleanup:
ZSTD_freeCCtx(cctx);
free(workBuf);
return result;
}
/**
* @memberof ktxTexture2
* @~English
* @brief Deflate the data in a ktxTexture2 object using miniz (ZLIB).
*
* The texture's levelIndex, dataSize, DFD, data pointer, and supercompressionScheme will
* all be updated after successful deflation to reflect the deflated data.
*
* @param[in] This pointer to the ktxTexture2 object of interest.
* @param[in] compressionLevel set speed vs compression ratio trade-off. Values
* between 1 and 9 are accepted. The lower the level the faster.
*/
KTX_error_code
ktxTexture2_DeflateZLIB(ktxTexture2* This, ktx_uint32_t compressionLevel)
{
ktx_uint32_t levelIndexByteLength =
This->numLevels * sizeof(ktxLevelIndexEntry);
ktx_uint8_t* workBuf;
ktx_uint8_t* cmpData;
ktx_size_t dstRemainingByteLength = 0;
ktx_size_t byteLengthCmp = 0;
ktx_size_t levelOffset = 0;
ktxLevelIndexEntry* cindex = This->_private->_levelIndex;
ktxLevelIndexEntry* nindex;
ktx_uint8_t* pCmpDst;
if (This->supercompressionScheme != KTX_SS_NONE)
return KTX_INVALID_OPERATION;
// On rare occasions the deflated data can be a few bytes larger than
// the source data. Calculating the dst buffer size using
// mz_deflateBound provides a conservative size to account for that.
for (int32_t level = This->numLevels - 1; level >= 0; level--) {
dstRemainingByteLength += ktxCompressZLIBBounds(cindex[level].byteLength);
}
workBuf = malloc(dstRemainingByteLength + levelIndexByteLength);
if (workBuf == NULL)
return KTX_OUT_OF_MEMORY;
nindex = (ktxLevelIndexEntry*)workBuf;
pCmpDst = &workBuf[levelIndexByteLength];
for (int32_t level = This->numLevels - 1; level >= 0; level--) {
size_t levelByteLengthCmp = dstRemainingByteLength;
KTX_error_code result = ktxCompressZLIBInt(pCmpDst + levelOffset,
&levelByteLengthCmp,
&This->pData[cindex[level].byteOffset],
cindex[level].byteLength,
compressionLevel);
if (result != KTX_SUCCESS) {
free(workBuf);
return result;
}
nindex[level].byteOffset = levelOffset;
nindex[level].uncompressedByteLength = cindex[level].byteLength;
nindex[level].byteLength = levelByteLengthCmp;
byteLengthCmp += levelByteLengthCmp;
levelOffset += levelByteLengthCmp;
dstRemainingByteLength -= levelByteLengthCmp;
}
// Move the compressed data into a correctly sized buffer.
cmpData = malloc(byteLengthCmp);
if (cmpData == NULL) {
free(workBuf);
return KTX_OUT_OF_MEMORY;
}
// Now modify the texture.
memcpy(cmpData, pCmpDst, byteLengthCmp); // Copy data to sized buffer.
memcpy(cindex, nindex, levelIndexByteLength); // Update level index
free(workBuf);
free(This->pData);
This->pData = cmpData;
This->dataSize = byteLengthCmp;
This->supercompressionScheme = KTX_SS_ZLIB;
This->_private->_requiredLevelAlignment = 1;
return KTX_SUCCESS;
}
/** @} */