#include <inttypes.h>
#include <zstd.h>
#include <KHR/khr_df.h>
#include "ktx.h"
#include "ktxint.h"
#include "texture2.h"
#include "vkformat_enum.h"
#include "vk_format.h"
#include "basis_sgd.h"
#if (EMSCRIPTEN)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif
#include "basisu/encoder/basisu_comp.h"
#if (EMSCRIPTEN)
#pragma clang diagnostic pop
#endif
#include "basisu/transcoder/basisu_file_headers.h"
#include "basisu/transcoder/basisu_transcoder.h"
#include "dfdutils/dfd.h"
using namespace basisu;
using namespace basist;
typedef struct ktxBasisParamsV1 {
ktx_uint32_t structSize;
ktx_uint32_t threadCount;
ktx_uint32_t compressionLevel;
ktx_uint32_t qualityLevel;
ktx_uint32_t maxEndpoints;
float endpointRDOThreshold;
ktx_uint32_t maxSelectors;
float selectorRDOThreshold;
ktx_bool_t normalMap;
ktx_bool_t separateRGToRGB_A;
ktx_bool_t preSwizzle;
ktx_bool_t noEndpointRDO;
ktx_bool_t noSelectorRDO;
} ktxBasisParamsV1;
enum swizzle_e {
R = 1,
G = 2,
B = 3,
A = 4,
ZERO = 5,
ONE = 6,
};
typedef void
(* PFNBUCOPYCB)(uint8_t* rgbadst, uint8_t* rgbasrc, uint32_t src_len,
ktx_size_t image_size, swizzle_e swizzle[4]);
static void
copy_rgba_to_rgba(uint8_t* rgbadst, uint8_t* rgbasrc, uint32_t,
ktx_size_t image_size, swizzle_e[4])
{
memcpy(rgbadst, rgbasrc, image_size);
}
static void
copy_rgb_to_rgba(uint8_t* rgbadst, uint8_t* rgbsrc, uint32_t,
ktx_size_t image_size, swizzle_e[4])
{
for (ktx_size_t i = 0; i < image_size; i += 3) {
memcpy(rgbadst, rgbsrc, 3);
rgbadst[3] = 0xff; rgbadst += 4; rgbsrc += 3;
}
}
void
swizzle_to_rgba(uint8_t* rgbadst, uint8_t* rgbasrc, uint32_t src_len,
ktx_size_t image_size, swizzle_e swizzle[4])
{
for (ktx_size_t i = 0; i < image_size; i += src_len) {
for (uint32_t c = 0; c < 4; c++) {
switch (swizzle[c]) {
case R:
rgbadst[c] = rgbasrc[0];
break;
case G:
rgbadst[c] = rgbasrc[1];
break;
case B:
rgbadst[c] = rgbasrc[2];
break;
case A:
rgbadst[c] = rgbasrc[3];
break;
case ZERO:
rgbadst[c] = 0x00;
break;
case ONE:
rgbadst[c] = 0xff;
break;
default:
assert(false);
}
}
rgbadst +=4; rgbasrc += src_len;
}
}
#if 0#endif
static KTX_error_code
ktxTexture2_rewriteDfd4BasisLzETC1S(ktxTexture2* This,
alpha_content_e alphaContent,
bool isLuminance)
{
uint32_t* cdfd = This->pDfd;
uint32_t* cbdb = cdfd + 1;
uint32_t newSampleCount = alphaContent != eNone ? 2 : 1;
uint32_t ndbSize = KHR_DF_WORD_SAMPLESTART
+ newSampleCount * KHR_DF_WORD_SAMPLEWORDS;
ndbSize *= sizeof(uint32_t);
uint32_t ndfdSize = ndbSize + 1 * sizeof(uint32_t);
uint32_t* ndfd = (uint32_t *)malloc(ndfdSize);
uint32_t* nbdb = ndfd + 1;
if (!ndfd)
return KTX_OUT_OF_MEMORY;
*ndfd = ndfdSize;
KHR_DFDSETVAL(nbdb, VENDORID, KHR_DF_VENDORID_KHRONOS);
KHR_DFDSETVAL(nbdb, DESCRIPTORTYPE, KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT);
KHR_DFDSETVAL(nbdb, VERSIONNUMBER, KHR_DF_VERSIONNUMBER_LATEST);
KHR_DFDSETVAL(nbdb, DESCRIPTORBLOCKSIZE, ndbSize);
KHR_DFDSETVAL(nbdb, MODEL, KHR_DF_MODEL_ETC1S);
KHR_DFDSETVAL(nbdb, PRIMARIES, KHR_DFDVAL(cbdb, PRIMARIES));
KHR_DFDSETVAL(nbdb, TRANSFER, KHR_DFDVAL(cbdb, TRANSFER));
KHR_DFDSETVAL(nbdb, FLAGS, KHR_DFDVAL(cbdb, FLAGS));
nbdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0] =
3 | (3 << KHR_DF_SHIFT_TEXELBLOCKDIMENSION1);
nbdb[KHR_DF_WORD_BYTESPLANE0] = 0;
nbdb[KHR_DF_WORD_BYTESPLANE4] = 0;
for (uint32_t sample = 0; sample < newSampleCount; sample++) {
uint16_t channelId, bitOffset;
if (sample == 0) {
bitOffset = 0;
if (getDFDNumComponents(cdfd) < 3 && !isLuminance)
channelId = KHR_DF_CHANNEL_ETC1S_RRR;
else
channelId = KHR_DF_CHANNEL_ETC1S_RGB;
} else {
assert(sample == 1 && alphaContent != eNone);
bitOffset = 64;
if (alphaContent == eAlpha)
channelId = KHR_DF_CHANNEL_ETC1S_AAA;
else if (alphaContent == eGreen)
channelId = KHR_DF_CHANNEL_ETC1S_GGG;
else channelId = KHR_DF_CHANNEL_ETC1S_RGB;
}
KHR_DFDSETSVAL(nbdb, sample, CHANNELID, channelId);
KHR_DFDSETSVAL(nbdb, sample, QUALIFIERS, 0);
KHR_DFDSETSVAL(nbdb, sample, SAMPLEPOSITION_ALL, 0);
KHR_DFDSETSVAL(nbdb, sample, BITOFFSET, bitOffset);
KHR_DFDSETSVAL(nbdb, sample, BITLENGTH, 63);
KHR_DFDSETSVAL(nbdb, sample, SAMPLELOWER, 0);
KHR_DFDSETSVAL(nbdb, sample, SAMPLEUPPER, UINT32_MAX);
}
This->pDfd = ndfd;
free(cdfd);
return KTX_SUCCESS;
}
static KTX_error_code
ktxTexture2_rewriteDfd4Uastc(ktxTexture2* This,
alpha_content_e alphaContent)
{
uint32_t* cdfd = This->pDfd;
uint32_t* cbdb = cdfd + 1;
uint32_t ndbSize = KHR_DF_WORD_SAMPLESTART
+ 1 * KHR_DF_WORD_SAMPLEWORDS;
ndbSize *= sizeof(uint32_t);
uint32_t ndfdSize = ndbSize + 1 * sizeof(uint32_t);
uint32_t* ndfd = (uint32_t *)malloc(ndfdSize);
uint32_t* nbdb = ndfd + 1;
if (!ndfd)
return KTX_OUT_OF_MEMORY;
*ndfd = ndfdSize;
KHR_DFDSETVAL(nbdb, VENDORID, KHR_DF_VENDORID_KHRONOS);
KHR_DFDSETVAL(nbdb, DESCRIPTORTYPE, KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT);
KHR_DFDSETVAL(nbdb, VERSIONNUMBER, KHR_DF_VERSIONNUMBER_LATEST);
KHR_DFDSETVAL(nbdb, DESCRIPTORBLOCKSIZE, ndbSize);
KHR_DFDSETVAL(nbdb, MODEL, KHR_DF_MODEL_UASTC);
KHR_DFDSETVAL(nbdb, PRIMARIES, KHR_DFDVAL(cbdb, PRIMARIES));
KHR_DFDSETVAL(nbdb, TRANSFER, KHR_DFDVAL(cbdb, TRANSFER));
KHR_DFDSETVAL(nbdb, FLAGS, KHR_DFDVAL(cbdb, FLAGS));
nbdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0] =
3 | (3 << KHR_DF_SHIFT_TEXELBLOCKDIMENSION1);
nbdb[KHR_DF_WORD_BYTESPLANE0] = 16;
nbdb[KHR_DF_WORD_BYTESPLANE4] = 0;
uint16_t channelId;
if (alphaContent == eAlpha) {
channelId = KHR_DF_CHANNEL_UASTC_RGBA;
} else if (alphaContent == eGreen) {
channelId = KHR_DF_CHANNEL_UASTC_RG;
} else if (getDFDNumComponents(cdfd) == 1) {
channelId = KHR_DF_CHANNEL_UASTC_RRR;
} else {
channelId = KHR_DF_CHANNEL_UASTC_RGB;
}
KHR_DFDSETSVAL(nbdb, 0, CHANNELID, channelId);
KHR_DFDSETSVAL(nbdb, 0, QUALIFIERS, 0);
KHR_DFDSETSVAL(nbdb, 0, SAMPLEPOSITION_ALL, 0);
KHR_DFDSETSVAL(nbdb, 0, BITOFFSET, 0);
KHR_DFDSETSVAL(nbdb, 0, BITLENGTH, 127);
KHR_DFDSETSVAL(nbdb, 0, SAMPLELOWER, 0);
KHR_DFDSETSVAL(nbdb, 0, SAMPLEUPPER, UINT32_MAX);
This->pDfd = ndfd;
free(cdfd);
return KTX_SUCCESS;
}
static bool basisuEncoderInitialized = false;
extern "C" KTX_error_code
ktxTexture2_CompressBasisEx(ktxTexture2* This, ktxBasisParams* params)
{
KTX_error_code result;
if (!params)
return KTX_INVALID_VALUE;
if (params->structSize != sizeof(struct ktxBasisParams))
return KTX_INVALID_VALUE;
if (This->supercompressionScheme != KTX_SS_NONE)
return KTX_INVALID_OPERATION;
if (This->isCompressed)
return KTX_INVALID_OPERATION;
if (This->_protected->_formatSize.flags & KTX_FORMAT_SIZE_PACKED_BIT)
return KTX_INVALID_OPERATION;
const uint32_t* BDB = This->pDfd+1;
uint32_t num_components, component_size;
getDFDComponentInfoUnpacked(This->pDfd, &num_components, &component_size);
if (component_size != 1)
return KTX_INVALID_OPERATION;
if (params->separateRGToRGB_A && num_components == 1)
return KTX_INVALID_OPERATION;
if (This->pData == NULL) {
result = ktxTexture2_LoadImageData(This, NULL, 0);
if (result != KTX_SUCCESS)
return result;
}
if (!basisuEncoderInitialized) {
basisu_encoder_init();
basisuEncoderInitialized = true;
}
basis_compressor_params cparams;
cparams.m_read_source_images = false; cparams.m_write_output_basis_files = false; cparams.m_status_output = params->verbose;
uint32_t layersFaces = This->numLayers * This->numFaces;
uint32_t num_images = 0;
for (uint32_t level = 1; level <= This->numLevels; level++) {
num_images += layersFaces * MAX(This->baseDepth >> (level - 1), 1);
}
cparams.m_source_images.resize(num_images);
basisu::vector<image>::iterator iit = cparams.m_source_images.begin();
swizzle_e meta_mapping[4] = {};
swizzle_e rg_to_rgba_mapping_etc1s[4] = { R, R, R, G };
swizzle_e rg_to_rgba_mapping_uastc[4] = { R, G, ZERO, ONE };
swizzle_e r_to_rgba_mapping[4] = { R, R, R, ONE };
swizzle_e* comp_mapping = 0;
alpha_content_e alphaContent = eNone;
bool isLuminance = false;
if (num_components == 1) {
comp_mapping = r_to_rgba_mapping;
} else if (num_components == 2) {
if (params->uastc)
comp_mapping = rg_to_rgba_mapping_uastc;
else {
comp_mapping = rg_to_rgba_mapping_etc1s;
alphaContent = eGreen;
}
} else if (num_components == 4) {
alphaContent = eAlpha;
}
std::string swizzleString = params->inputSwizzle;
if (params->preSwizzle) {
if (swizzleString.size() > 0) {
return KTX_INVALID_OPERATION;
}
ktxHashListEntry* swizzleEntry;
result = ktxHashList_FindEntry(&This->kvDataHead, KTX_SWIZZLE_KEY,
&swizzleEntry);
if (result == KTX_SUCCESS) {
ktx_uint32_t swizzleLen = 0;
char* swizzleStr = nullptr;
ktxHashListEntry_GetValue(swizzleEntry,
&swizzleLen, (void**)&swizzleStr);
ktxHashList_DeleteEntry(&This->kvDataHead, swizzleEntry);
swizzleString.resize(swizzleLen);
swizzleString.assign(swizzleStr, swizzleLen);
}
}
if (swizzleString.size() > 0) {
if (num_components < 3
|| (num_components == 3 && swizzleString.compare("rgb1"))
|| (num_components == 4 && swizzleString.compare("rgba"))) {
for (int i = 0; i < 4; i++) {
switch (swizzleString[i]) {
case 'r': meta_mapping[i] = R; break;
case 'g': meta_mapping[i] = G; break;
case 'b': meta_mapping[i] = B; break;
case 'a': meta_mapping[i] = A; break;
case '0': meta_mapping[i] = ZERO; break;
case '1': meta_mapping[i] = ONE; break;
}
}
comp_mapping = meta_mapping;
}
int i;
for (i = 0; i < 3; i++) {
if (meta_mapping[i] != r_to_rgba_mapping[i])
break;
}
if (i == 3) {
isLuminance = true;
}
if (meta_mapping[3] != ONE) {
alphaContent = eAlpha;
}
}
PFNBUCOPYCB copycb = copy_rgba_to_rgba; if (comp_mapping) {
copycb = swizzle_to_rgba;
} else {
switch (num_components) {
case 4: copycb = copy_rgba_to_rgba; break;
case 3: copycb = copy_rgb_to_rgba; break;
default: assert(false);
}
}
for (uint32_t level = 0; level < This->numLevels; level++) {
uint32_t width = MAX(1, This->baseWidth >> level);
uint32_t height = MAX(1, This->baseHeight >> level);
uint32_t depth = MAX(1, This->baseDepth >> level);
ktx_size_t image_size = ktxTexture2_GetImageSize(This, level);
for (uint32_t layer = 0; layer < This->numLayers; layer++) {
uint32_t faceSlices = This->numFaces == 1 ? depth : This->numFaces;
for (ktx_uint32_t slice = 0; slice < faceSlices; slice++) {
ktx_size_t offset;
ktxTexture2_GetImageOffset(This, level, layer, slice, &offset);
iit->resize(width, height);
copycb((uint8_t*)iit->get_ptr(), This->pData + offset,
num_components, image_size,
comp_mapping);
++iit;
}
}
}
free(This->pData); This->pData = NULL;
This->dataSize = 0;
basist::etc1_global_selector_codebook sel_codebook(basist::g_global_selector_cb_size,
basist::g_global_selector_cb);
ktx_uint32_t threadCount = params->threadCount;
if (threadCount < 1)
threadCount = 1;
job_pool jpool(threadCount);
cparams.m_pJob_pool = &jpool;
#if BASISU_SUPPORT_SSE
bool prevSSESupport = g_cpu_supports_sse41;
if (params->noSSE)
g_cpu_supports_sse41 = false;
#endif
cparams.m_uastc = params->uastc;
if (params->uastc) {
cparams.m_pack_uastc_flags = params->uastcFlags;
if (params->uastcRDO) {
cparams.m_rdo_uastc = true;
if (params->uastcRDOQualityScalar > 0.0f) {
cparams.m_rdo_uastc_quality_scalar =
params->uastcRDOQualityScalar;
}
if (params->uastcRDODictSize > 0) {
cparams.m_rdo_uastc_dict_size = params->uastcRDODictSize;
}
if (params->uastcRDOMaxSmoothBlockErrorScale > 0) {
cparams.m_rdo_uastc_max_smooth_block_error_scale =
params->uastcRDOMaxSmoothBlockErrorScale;
}
if (params->uastcRDOMaxSmoothBlockStdDev > 0) {
cparams.m_rdo_uastc_smooth_block_max_std_dev =
params->uastcRDOMaxSmoothBlockStdDev;
}
cparams.m_rdo_uastc_favor_simpler_modes_in_rdo_mode =
!params->uastcRDODontFavorSimplerModes;
cparams.m_rdo_uastc_favor_simpler_modes_in_rdo_mode =
!params->uastcRDONoMultithreading;
}
} else {
ktx_uint32_t transfer = KHR_DFDVAL(BDB, TRANSFER);
if (transfer == KHR_DF_TRANSFER_SRGB)
cparams.m_perceptual = true;
else
cparams.m_perceptual = false;
cparams.m_mip_gen = false;
cparams.m_compression_level = params->compressionLevel;
if (params->maxEndpoints && params->maxSelectors) {
cparams.m_max_endpoint_clusters = params->maxEndpoints;
cparams.m_max_selector_clusters = params->maxSelectors;
} else if (params->qualityLevel != 0) {
cparams.m_max_endpoint_clusters = 0;
cparams.m_max_selector_clusters = 0;
cparams.m_quality_level = params->qualityLevel;
} else {
cparams.m_max_endpoint_clusters = 0;
cparams.m_max_selector_clusters = 0;
cparams.m_quality_level = 128;
}
if (params->endpointRDOThreshold > 0)
cparams.m_endpoint_rdo_thresh = params->endpointRDOThreshold;
if (params->selectorRDOThreshold > 0)
cparams.m_selector_rdo_thresh = params->selectorRDOThreshold;
if (params->normalMap) {
cparams.m_no_endpoint_rdo = true;
cparams.m_no_selector_rdo = true;
} else {
cparams.m_no_endpoint_rdo = params->noEndpointRDO;
cparams.m_no_selector_rdo = params->noSelectorRDO;
}
cparams.m_pSel_codebook = &sel_codebook;
}
if (This->isVideo) {
cparams.m_tex_type = cBASISTexTypeVideoFrames;
} else {
cparams.m_tex_type = cBASISTexType2D;
}
#define DUMP_BASIS_FILE 0
#if DUMP_BASIS_FILE
cparams.m_out_filename = "ktxtest.basis";
cparams.m_write_output_basis_files = true;
#endif
#define DEBUG_ENCODER 0
#if DEBUG_ENCODER
cparams.m_debug = true;
g_debug_printf = true;
#endif
basis_compressor c;
(void)c.init(cparams);
basis_compressor::error_code ec = c.process();
if (ec != basis_compressor::cECSuccess) {
assert(ec != basis_compressor::cECFailedValidating);
return KTX_INVALID_OPERATION;
#if 0#endif
}
#if DUMP_BASIS_FILE
return KTX_UNSUPPORTED_FEATURE;
#endif
#if BASISU_SUPPORT_SSE
g_cpu_supports_sse41 = prevSSESupport;
#endif
const uint8_vec& bf = c.get_output_basis_file();
const basis_file_header& bfh = *reinterpret_cast<const basis_file_header*>(bf.data());
assert(bfh.m_total_images == num_images);
uint8_t* bgd = nullptr;
size_t bgd_size;
uint32_t image_data_size = 0;
ktxTexture2_private& priv = *This->_private;
uint32_t base_offset = bfh.m_slice_desc_file_ofs;
const basis_slice_desc* slice
= reinterpret_cast<const basis_slice_desc*>(&bf[base_offset]);
std::vector<uint32_t> level_file_offsets(This->numLevels);
if (params->uastc) {
for (uint32_t level = 0; level < This->numLevels; level++) {
uint32_t depth = MAX(1, This->baseDepth >> level);
uint32_t levelByteLength = 0;
uint32_t levelImageCount = This->numLayers * This->numFaces * depth;
level_file_offsets[level] = slice->m_file_ofs;
for (uint32_t image = 0; image < levelImageCount; image++, slice++) {
image_data_size += slice->m_file_size;
levelByteLength += slice->m_file_size;
}
priv._levelIndex[level].byteLength = levelByteLength;
priv._levelIndex[level].uncompressedByteLength = levelByteLength;
}
} else {
uint32_t image_desc_size = sizeof(ktxBasisLzEtc1sImageDesc);
bgd_size = sizeof(ktxBasisLzGlobalHeader)
+ image_desc_size * num_images
+ bfh.m_endpoint_cb_file_size + bfh.m_selector_cb_file_size
+ bfh.m_tables_file_size;
bgd = new ktx_uint8_t[bgd_size];
ktxBasisLzGlobalHeader& bgdh = *reinterpret_cast<ktxBasisLzGlobalHeader*>(bgd);
bgdh.endpointCount = (uint16_t)bfh.m_total_endpoints;
bgdh.endpointsByteLength = bfh.m_endpoint_cb_file_size;
bgdh.selectorCount = (uint16_t)bfh.m_total_selectors;
bgdh.selectorsByteLength = bfh.m_selector_cb_file_size;
bgdh.tablesByteLength = bfh.m_tables_file_size;
bgdh.extendedByteLength = 0;
ktxBasisLzEtc1sImageDesc* kimages = BGD_ETC1S_IMAGE_DESCS(bgd);
uint32_t image = 0;
for (uint32_t level = 0; level < This->numLevels; level++) {
uint32_t depth = MAX(1, This->baseDepth >> level);
uint32_t level_byte_length = 0;
assert(!(slice->m_flags & cSliceDescFlagsHasAlpha));
level_file_offsets[level] = slice->m_file_ofs;
for (uint32_t layer = 0; layer < This->numLayers; layer++) {
uint32_t faceSlices = This->numFaces == 1 ? depth
: This->numFaces;
for (uint32_t faceSlice = 0; faceSlice < faceSlices; faceSlice++) {
level_byte_length += slice->m_file_size;
kimages[image].rgbSliceByteOffset = slice->m_file_ofs
- level_file_offsets[level];
kimages[image].rgbSliceByteLength = slice->m_file_size;
if (bfh.m_flags & cBASISHeaderFlagHasAlphaSlices) {
slice++;
level_byte_length += slice->m_file_size;
kimages[image].alphaSliceByteOffset =
slice->m_file_ofs - level_file_offsets[level];
kimages[image].alphaSliceByteLength =
slice->m_file_size;
} else {
kimages[image].alphaSliceByteOffset = 0;
kimages[image].alphaSliceByteLength = 0;
}
if (This->isVideo) {
kimages[image].imageFlags =
(slice->m_flags & ~cSliceDescFlagsHasAlpha);
kimages[image].imageFlags ^=
cSliceDescFlagsFrameIsIFrame;
} else {
kimages[image].imageFlags = 0;
}
slice++;
image++;
}
}
priv._levelIndex[level].byteLength = level_byte_length;
priv._levelIndex[level].uncompressedByteLength = 0;
image_data_size += level_byte_length;
}
uint8_t* dstptr = reinterpret_cast<uint8_t*>(&kimages[image]);
memcpy(dstptr,
&bf[bfh.m_endpoint_cb_file_ofs],
bfh.m_endpoint_cb_file_size);
dstptr += bgdh.endpointsByteLength;
memcpy(dstptr,
&bf[bfh.m_selector_cb_file_ofs],
bfh.m_selector_cb_file_size);
dstptr += bgdh.selectorsByteLength;
memcpy(dstptr,
&bf[bfh.m_tables_file_ofs],
bfh.m_tables_file_size);
assert((size_t)(dstptr + bgdh.tablesByteLength - bgd) <= bgd_size);
priv._supercompressionGlobalData = bgd;
priv._sgdByteLength = bgd_size;
}
uint8_t* new_data = 0;
ktxFormatSize& formatSize = This->_protected->_formatSize;
uint64_t level_offset = 0;
if ((bfh.m_flags & cBASISHeaderFlagHasAlphaSlices) == 0) {
alphaContent = eNone;
}
new_data = (uint8_t*) malloc(image_data_size);
if (!new_data) {
result = KTX_OUT_OF_MEMORY;
goto cleanup;
}
if (params->uastc) {
result = ktxTexture2_rewriteDfd4Uastc(This, alphaContent);
if (result != KTX_SUCCESS) goto cleanup;
ktxFormatSize_initFromDfd(&formatSize, This->pDfd);
priv._requiredLevelAlignment = 4 * 4;
} else {
result = ktxTexture2_rewriteDfd4BasisLzETC1S(This, alphaContent,
isLuminance);
if (result != KTX_SUCCESS) goto cleanup;
This->supercompressionScheme = KTX_SS_BASIS_LZ;
ktxFormatSize_initFromDfd(&formatSize, This->pDfd);
priv._requiredLevelAlignment = 1;
}
This->vkFormat = VK_FORMAT_UNDEFINED;
assert(This->_protected->_typeSize == 1);
This->pData = new_data;
This->dataSize = image_data_size;
for (int32_t level = This->numLevels - 1; level >= 0; level--) {
priv._levelIndex[level].byteOffset = level_offset;
memcpy(This->pData + level_offset,
&bf[level_file_offsets[level]],
priv._levelIndex[level].byteLength);
level_offset += _KTX_PADN(priv._requiredLevelAlignment,
priv._levelIndex[level].byteLength);
}
return KTX_SUCCESS;
cleanup:
if (bgd) {
delete bgd;
priv._supercompressionGlobalData = 0;
priv._sgdByteLength = 0;
}
if (new_data) delete new_data;
return result;
}
extern "C" KTX_API const ktx_uint32_t KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL
= BASISU_DEFAULT_COMPRESSION_LEVEL;
extern "C" KTX_error_code
ktxTexture2_CompressBasis(ktxTexture2* This, ktx_uint32_t quality)
{
ktxBasisParams params = {};
params.structSize = sizeof(params);
params.threadCount = 1;
params.compressionLevel = KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL;
params.qualityLevel = quality;
return ktxTexture2_CompressBasisEx(This, ¶ms);
}