You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.0 KiB
211 lines
6.0 KiB
/* |
|
img_ktx2.c - ktx2 format load |
|
Copyright (C) 2023 Provod |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
*/ |
|
|
|
#include "imagelib.h" |
|
#include "xash3d_mathlib.h" |
|
#include "img_ktx2.h" |
|
|
|
static void Image_KTX2Format( uint32_t ktx2_format ) |
|
{ |
|
switch( ktx2_format ) |
|
{ |
|
case KTX2_FORMAT_BC4_UNORM_BLOCK: |
|
image.type = PF_BC4_UNSIGNED; |
|
// 1 component for ref_gl |
|
break; |
|
case KTX2_FORMAT_BC4_SNORM_BLOCK: |
|
image.type = PF_BC4_SIGNED; |
|
// 1 component for ref_gl |
|
break; |
|
case KTX2_FORMAT_BC5_UNORM_BLOCK: |
|
image.type = PF_BC5_UNSIGNED; |
|
// 2 components for ref_gl |
|
SetBits( image.flags, IMAGE_HAS_ALPHA ); |
|
break; |
|
case KTX2_FORMAT_BC5_SNORM_BLOCK: |
|
image.type = PF_BC5_SIGNED; |
|
// 2 components for ref_gl |
|
SetBits( image.flags, IMAGE_HAS_ALPHA ); |
|
break; |
|
case KTX2_FORMAT_BC6H_UFLOAT_BLOCK: |
|
image.type = PF_BC6H_UNSIGNED; |
|
// 3 components for ref_gl |
|
SetBits( image.flags, IMAGE_HAS_COLOR ); |
|
break; |
|
case KTX2_FORMAT_BC6H_SFLOAT_BLOCK: |
|
image.type = PF_BC6H_SIGNED; |
|
// 3 components for ref_gl |
|
SetBits( image.flags, IMAGE_HAS_COLOR ); |
|
break; |
|
case KTX2_FORMAT_BC7_UNORM_BLOCK: |
|
image.type = PF_BC7_UNORM; |
|
// 4 components for ref_gl |
|
SetBits( image.flags, IMAGE_HAS_COLOR | IMAGE_HAS_ALPHA ); |
|
break; |
|
case KTX2_FORMAT_BC7_SRGB_BLOCK: |
|
image.type = PF_BC7_SRGB; |
|
// 4 components for ref_gl |
|
SetBits( image.flags, IMAGE_HAS_COLOR | IMAGE_HAS_ALPHA ); |
|
break; |
|
default: |
|
image.type = PF_UNKNOWN; |
|
break; |
|
} |
|
} |
|
|
|
static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer, fs_offset_t filesize ) |
|
{ |
|
ktx2_index_t index; |
|
size_t total_size = 0; |
|
size_t max_offset = 0; |
|
int mip, cursor; |
|
const byte *const levels_begin = buffer + KTX2_LEVELS_OFFSET; |
|
|
|
// Sets image.type and image.flags |
|
Image_KTX2Format( header->vkFormat ); |
|
|
|
if( image.type == PF_UNKNOWN ) |
|
{ |
|
Con_DPrintf( S_ERROR "%s: unsupported KTX2 format %d\n", __FUNCTION__, header->vkFormat ); |
|
return false; |
|
} |
|
|
|
if( !Image_CheckFlag( IL_DDS_HARDWARE ) && ImageCompressed( image.type )) |
|
{ |
|
Con_DPrintf( S_WARN "%s: has compressed format, but support is not advertized\n", __FUNCTION__ ); |
|
return false; |
|
} |
|
|
|
if( header->levelCount == 0 ) |
|
{ |
|
Con_DPrintf( S_ERROR "%s: file has no mip levels\n", __FUNCTION__ ); |
|
return false; |
|
} |
|
|
|
if( header->pixelDepth > 1 ) |
|
{ |
|
Con_DPrintf( S_ERROR "%s: unsupported KTX2 pixelDepth %d\n", __FUNCTION__, header->pixelDepth ); |
|
return false; |
|
} |
|
|
|
if( header->faceCount > 1 ) |
|
{ |
|
Con_DPrintf( S_ERROR "%s: unsupported KTX2 faceCount %d\n", __FUNCTION__, header->faceCount ); |
|
return false; |
|
} |
|
|
|
if( header->layerCount > 1 ) |
|
{ |
|
Con_DPrintf( S_ERROR "%s: unsupported KTX2 layerCount %d\n", __FUNCTION__, header->layerCount ); |
|
return false; |
|
} |
|
|
|
if( header->supercompressionScheme != 0 ) |
|
{ |
|
Con_DPrintf( S_ERROR "%s: unsupported KTX2 supercompressionScheme %d\n", __FUNCTION__, header->supercompressionScheme ); |
|
return false; |
|
} |
|
|
|
if( header->levelCount * sizeof( ktx2_level_t ) + KTX2_LEVELS_OFFSET > filesize ) |
|
{ |
|
Con_DPrintf( S_ERROR "%s: file abruptly ends\n", __FUNCTION__ ); |
|
return false; |
|
} |
|
|
|
memcpy( &index, buffer + KTX2_IDENTIFIER_SIZE + sizeof( ktx2_header_t ), sizeof( index )); |
|
|
|
for( mip = 0; mip < header->levelCount; ++mip ) |
|
{ |
|
const uint32_t width = Q_max( 1, ( header->pixelWidth >> mip )); |
|
const uint32_t height = Q_max( 1, ( header->pixelHeight >> mip )); |
|
const uint32_t mip_size = Image_ComputeSize( image.type, width, height, image.depth ); |
|
|
|
ktx2_level_t level; |
|
memcpy( &level, levels_begin + mip * sizeof( level ), sizeof( level )); |
|
|
|
if( mip_size != level.byteLength ) |
|
{ |
|
Con_DPrintf( S_ERROR "%s: mip=%d size mismatch read=%d, but computed=%d\n", |
|
__FUNCTION__, mip, (int)level.byteLength, mip_size ); |
|
return false; |
|
} |
|
|
|
total_size += level.byteLength; |
|
max_offset = Q_max( max_offset, level.byteLength + level.byteOffset ); |
|
} |
|
|
|
if( max_offset > filesize ) |
|
return false; |
|
|
|
image.size = total_size; |
|
image.num_mips = header->levelCount; |
|
|
|
image.rgba = Mem_Malloc( host.imagepool, image.size ); |
|
memcpy( image.rgba, buffer, image.size ); |
|
|
|
for( mip = 0, cursor = 0; mip < header->levelCount; ++mip ) |
|
{ |
|
ktx2_level_t level; |
|
memcpy( &level, levels_begin + mip * sizeof( level ), sizeof( level )); |
|
memcpy( image.rgba + cursor, buffer + level.byteOffset, level.byteLength ); |
|
cursor += level.byteLength; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
qboolean Image_LoadKTX2( const char *name, const byte *buffer, fs_offset_t filesize ) |
|
{ |
|
ktx2_header_t header; |
|
|
|
if( filesize < KTX2_MINIMAL_HEADER_SIZE ) |
|
return false; |
|
|
|
if( memcmp( buffer, KTX2_IDENTIFIER, KTX2_IDENTIFIER_SIZE ) != 0 ) |
|
{ |
|
Con_DPrintf( S_ERROR "%s: (%s) has invalid identifier\n", __FUNCTION__, name ); |
|
return false; |
|
} |
|
|
|
memcpy( &header, buffer + KTX2_IDENTIFIER_SIZE, sizeof( header )); |
|
|
|
image.width = header.pixelWidth; |
|
image.height = header.pixelHeight; |
|
image.depth = Q_max( 1, header.pixelDepth ); |
|
image.num_mips = 1; |
|
|
|
ClearBits( image.flags, IMAGE_HAS_COLOR | IMAGE_HAS_ALPHA | IMAGE_HAS_LUMA ); |
|
|
|
if( !Image_KTX2Parse( &header, buffer, filesize )) |
|
{ |
|
if( !Image_CheckFlag( IL_KTX2_RAW )) |
|
return false; |
|
|
|
// If KTX2 to imagelib conversion failed, try passing the file as raw data. |
|
// This is useful for ref_vk which can directly support hundreds of formats which we don't convert to pixformat_t here |
|
|
|
Con_DPrintf( S_WARN "%s: (%s) could not be converted to supported imagelib format, passing as raw KTX2 data\n", __FUNCTION__, name ); |
|
// This is a catch-all for ref_vk, which can do this format directly and natively |
|
image.type = PF_KTX2_RAW; |
|
|
|
image.size = filesize; |
|
//image.encode = TODO custom encode type? |
|
|
|
image.rgba = Mem_Malloc( host.imagepool, image.size ); |
|
memcpy( image.rgba, buffer, image.size ); |
|
} |
|
|
|
return true; |
|
}
|
|
|