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.
384 lines
9.1 KiB
384 lines
9.1 KiB
/* |
|
img_dds.c - dds format load |
|
Copyright (C) 2015 Uncle Mike |
|
|
|
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_dds.h" |
|
|
|
qboolean Image_CheckDXT3Alpha( dds_t *hdr, byte *fin ) |
|
{ |
|
word sAlpha; |
|
byte *alpha; |
|
int x, y, i, j; |
|
|
|
for( y = 0; y < hdr->dwHeight; y += 4 ) |
|
{ |
|
for( x = 0; x < hdr->dwWidth; x += 4 ) |
|
{ |
|
alpha = fin + 8; |
|
fin += 16; |
|
|
|
for( j = 0; j < 4; j++ ) |
|
{ |
|
sAlpha = alpha[2*j] + 256 * alpha[2*j+1]; |
|
|
|
for( i = 0; i < 4; i++ ) |
|
{ |
|
if((( x + i ) < hdr->dwWidth ) && (( y + j ) < hdr->dwHeight )) |
|
{ |
|
if( sAlpha == 0 ) |
|
return true; |
|
} |
|
sAlpha >>= 4; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
qboolean Image_CheckDXT5Alpha( dds_t *hdr, byte *fin ) |
|
{ |
|
uint bits, bitmask; |
|
byte *alphamask; |
|
int x, y, i, j; |
|
|
|
for( y = 0; y < hdr->dwHeight; y += 4 ) |
|
{ |
|
for( x = 0; x < hdr->dwWidth; x += 4 ) |
|
{ |
|
if( y >= hdr->dwHeight || x >= hdr->dwWidth ) |
|
break; |
|
|
|
alphamask = fin + 2; |
|
fin += 8; |
|
|
|
bitmask = ((uint *)fin)[1]; |
|
fin += 8; |
|
|
|
// last three bytes |
|
bits = (alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16); |
|
|
|
for( j = 2; j < 4; j++ ) |
|
{ |
|
for( i = 0; i < 4; i++ ) |
|
{ |
|
// only put pixels out < width or height |
|
if((( x + i ) < hdr->dwWidth ) && (( y + j ) < hdr->dwHeight )) |
|
{ |
|
if( bits & 0x07 ) |
|
return true; |
|
} |
|
bits >>= 3; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void Image_DXTGetPixelFormat( dds_t *hdr, dds_header_dxt10_t *headerExt ) |
|
{ |
|
uint bits = hdr->dsPixelFormat.dwRGBBitCount; |
|
|
|
if( !FBitSet( hdr->dsCaps.dwCaps2, DDS_VOLUME )) |
|
hdr->dwDepth = 1; |
|
|
|
if( FBitSet( hdr->dsPixelFormat.dwFlags, DDS_FOURCC )) |
|
{ |
|
if( hdr->dsPixelFormat.dwFourCC == TYPE_DX10 ) |
|
{ |
|
switch( headerExt->dxgiFormat ) |
|
{ |
|
case DXGI_FORMAT_BC6H_SF16: |
|
image.type = PF_BC6H_SIGNED; |
|
break; |
|
case DXGI_FORMAT_BC6H_UF16: |
|
case DXGI_FORMAT_BC6H_TYPELESS: |
|
image.type = PF_BC6H_UNSIGNED; |
|
break; |
|
case DXGI_FORMAT_BC7_UNORM: |
|
case DXGI_FORMAT_BC7_UNORM_SRGB: |
|
case DXGI_FORMAT_BC7_TYPELESS: |
|
image.type = PF_BC7; |
|
break; |
|
default: |
|
image.type = PF_UNKNOWN; |
|
break; |
|
} |
|
} |
|
else |
|
{ |
|
switch( hdr->dsPixelFormat.dwFourCC ) |
|
{ |
|
case TYPE_DXT1: |
|
image.type = PF_DXT1; |
|
break; |
|
case TYPE_DXT2: |
|
image.flags &= ~IMAGE_HAS_ALPHA; // alpha is already premultiplied by color |
|
// intentionally fallthrough |
|
case TYPE_DXT3: |
|
image.type = PF_DXT3; |
|
break; |
|
case TYPE_DXT4: |
|
image.flags &= ~IMAGE_HAS_ALPHA; // alpha is already premultiplied by color |
|
// intentionally fallthrough |
|
case TYPE_DXT5: |
|
image.type = PF_DXT5; |
|
break; |
|
case TYPE_ATI2: |
|
image.type = PF_ATI2; |
|
break; |
|
default: |
|
image.type = PF_UNKNOWN; // assume error |
|
break; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// this dds texture isn't compressed so write out ARGB or luminance format |
|
if( hdr->dsPixelFormat.dwFlags & DDS_DUDV ) |
|
{ |
|
image.type = PF_UNKNOWN; // assume error |
|
} |
|
else if( hdr->dsPixelFormat.dwFlags & DDS_LUMINANCE ) |
|
{ |
|
image.type = PF_UNKNOWN; // assume error |
|
} |
|
else |
|
{ |
|
switch( bits ) |
|
{ |
|
case 32: |
|
image.type = PF_BGRA_32; |
|
break; |
|
case 24: |
|
image.type = PF_BGR_24; |
|
break; |
|
case 8: |
|
image.type = PF_LUMINANCE; |
|
break; |
|
default: |
|
image.type = PF_UNKNOWN; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
// setup additional flags |
|
if( hdr->dsCaps.dwCaps1 & DDS_COMPLEX && hdr->dsCaps.dwCaps2 & DDS_CUBEMAP ) |
|
image.flags |= IMAGE_CUBEMAP; |
|
|
|
if( hdr->dwFlags & DDS_MIPMAPCOUNT ) |
|
image.num_mips = hdr->dwMipMapCount; // get actual mip count |
|
} |
|
|
|
size_t Image_DXTGetLinearSize( int type, int width, int height, int depth ) |
|
{ |
|
switch( type ) |
|
{ |
|
case PF_DXT1: return ((( width + 3 ) / 4 ) * (( height + 3 ) / 4 ) * depth * 8 ); |
|
case PF_DXT3: |
|
case PF_DXT5: |
|
case PF_BC6H_SIGNED: |
|
case PF_BC6H_UNSIGNED: |
|
case PF_BC7: |
|
case PF_ATI2: return ((( width + 3 ) / 4 ) * (( height + 3 ) / 4 ) * depth * 16 ); |
|
case PF_LUMINANCE: return (width * height * depth); |
|
case PF_BGR_24: |
|
case PF_RGB_24: return (width * height * depth * 3); |
|
case PF_BGRA_32: |
|
case PF_RGBA_32: return (width * height * depth * 4); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
size_t Image_DXTCalcMipmapSize( dds_t *hdr ) |
|
{ |
|
size_t buffsize = 0; |
|
int i, width, height; |
|
|
|
// now correct buffer size |
|
for( i = 0; i < Q_max( 1, ( hdr->dwMipMapCount )); i++ ) |
|
{ |
|
width = Q_max( 1, ( hdr->dwWidth >> i )); |
|
height = Q_max( 1, ( hdr->dwHeight >> i )); |
|
buffsize += Image_DXTGetLinearSize( image.type, width, height, image.depth ); |
|
} |
|
|
|
return buffsize; |
|
} |
|
|
|
uint Image_DXTCalcSize( const char *name, dds_t *hdr, size_t filesize ) |
|
{ |
|
size_t buffsize = 0; |
|
int w = image.width; |
|
int h = image.height; |
|
int d = image.depth; |
|
|
|
if( hdr->dsCaps.dwCaps2 & DDS_CUBEMAP ) |
|
{ |
|
// cubemap w*h always match for all sides |
|
buffsize = Image_DXTCalcMipmapSize( hdr ) * 6; |
|
} |
|
else if( hdr->dwFlags & DDS_MIPMAPCOUNT ) |
|
{ |
|
// if mipcount > 1 |
|
buffsize = Image_DXTCalcMipmapSize( hdr ); |
|
} |
|
else if( hdr->dwFlags & ( DDS_LINEARSIZE|DDS_PITCH )) |
|
{ |
|
// just in case (no need, really) |
|
buffsize = hdr->dwLinearSize; |
|
} |
|
else |
|
{ |
|
// pretty solution for microsoft bug |
|
buffsize = Image_DXTCalcMipmapSize( hdr ); |
|
} |
|
|
|
if( filesize != buffsize ) // main check |
|
{ |
|
Con_DPrintf( S_WARN "Image_LoadDDS: (%s) probably corrupted (%i should be %lu)\n", name, buffsize, filesize ); |
|
if( buffsize > filesize ) |
|
return false; |
|
} |
|
|
|
return buffsize; |
|
} |
|
|
|
void Image_DXTAdjustVolume( dds_t *hdr ) |
|
{ |
|
if( hdr->dwDepth <= 1 ) |
|
return; |
|
|
|
hdr->dwLinearSize = Image_DXTGetLinearSize( image.type, hdr->dwWidth, hdr->dwHeight, hdr->dwDepth ); |
|
hdr->dwFlags |= DDS_LINEARSIZE; |
|
} |
|
|
|
/* |
|
============= |
|
Image_LoadDDS |
|
============= |
|
*/ |
|
qboolean Image_LoadDDS( const char *name, const byte *buffer, fs_offset_t filesize ) |
|
{ |
|
dds_t header; |
|
byte *fin; |
|
int headersOffset; |
|
dds_header_dxt10_t header2; |
|
|
|
if( filesize < sizeof( header )) |
|
return false; |
|
|
|
memcpy( &header, buffer, sizeof( header )); |
|
|
|
if( header.dwIdent != DDSHEADER ) |
|
return false; // it's not a dds file, just skip it |
|
|
|
if( header.dwSize != sizeof( header ) - sizeof( uint )) // size of the structure (minus MagicNum) |
|
{ |
|
Con_DPrintf( S_ERROR "Image_LoadDDS: (%s) have corrupted header\n", name ); |
|
return false; |
|
} |
|
|
|
if( header.dsPixelFormat.dwSize != sizeof( dds_pixf_t )) // size of the structure |
|
{ |
|
Con_DPrintf( S_ERROR "Image_LoadDDS: (%s) have corrupt pixelformat header\n", name ); |
|
return false; |
|
} |
|
|
|
headersOffset = sizeof( header ); |
|
if( header.dsPixelFormat.dwFourCC == TYPE_DX10 ) |
|
{ |
|
memcpy( &header2, buffer + sizeof( header ), sizeof( header2 )); |
|
headersOffset += sizeof( header2 ); |
|
} |
|
|
|
image.width = header.dwWidth; |
|
image.height = header.dwHeight; |
|
|
|
if( header.dwFlags & DDS_DEPTH ) |
|
image.depth = header.dwDepth; |
|
else image.depth = 1; |
|
|
|
if( !Image_ValidSize( name )) return false; |
|
|
|
Image_DXTGetPixelFormat( &header, &header2 ); // and image type too :) |
|
Image_DXTAdjustVolume( &header ); |
|
|
|
if( !Image_CheckFlag( IL_DDS_HARDWARE ) && ImageDXT( image.type )) |
|
return false; // silently rejected |
|
|
|
if( image.type == PF_UNKNOWN ) |
|
{ |
|
Con_DPrintf( S_ERROR "Image_LoadDDS: (%s) has unrecognized type\n", name ); |
|
return false; |
|
} |
|
|
|
image.size = Image_DXTCalcSize( name, &header, filesize - headersOffset ); |
|
if( image.size == 0 ) return false; // just in case |
|
fin = (byte *)( buffer + headersOffset ); |
|
|
|
// copy an encode method |
|
image.encode = (word)header.dwReserved1[0]; |
|
|
|
switch( image.encode ) |
|
{ |
|
case DXT_ENCODE_COLOR_YCoCg: |
|
SetBits( image.flags, IMAGE_HAS_COLOR ); |
|
break; |
|
case DXT_ENCODE_NORMAL_AG_ORTHO: |
|
case DXT_ENCODE_NORMAL_AG_STEREO: |
|
case DXT_ENCODE_NORMAL_AG_PARABOLOID: |
|
case DXT_ENCODE_NORMAL_AG_QUARTIC: |
|
case DXT_ENCODE_NORMAL_AG_AZIMUTHAL: |
|
SetBits( image.flags, IMAGE_HAS_COLOR ); |
|
break; |
|
default: // check for real alpha-pixels |
|
if( image.type == PF_DXT3 && Image_CheckDXT3Alpha( &header, fin )) |
|
SetBits( image.flags, IMAGE_HAS_ALPHA ); |
|
else if( image.type == PF_DXT5 && Image_CheckDXT5Alpha( &header, fin )) |
|
SetBits( image.flags, IMAGE_HAS_ALPHA ); |
|
else if ( image.type == PF_BC7 ) |
|
SetBits(image.flags, IMAGE_HAS_ALPHA); |
|
if( !FBitSet( header.dsPixelFormat.dwFlags, DDS_LUMINANCE )) |
|
SetBits( image.flags, IMAGE_HAS_COLOR ); |
|
break; |
|
} |
|
|
|
if( image.type == PF_LUMINANCE ) |
|
ClearBits( image.flags, IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA ); |
|
|
|
if( header.dwReserved1[1] != 0 ) |
|
{ |
|
// store texture reflectivity |
|
image.fogParams[0] = ((header.dwReserved1[1] & 0x000000FF) >> 0 ); |
|
image.fogParams[1] = ((header.dwReserved1[1] & 0x0000FF00) >> 8 ); |
|
image.fogParams[2] = ((header.dwReserved1[1] & 0x00FF0000) >> 16); |
|
image.fogParams[3] = ((header.dwReserved1[1] & 0xFF000000) >> 24); |
|
} |
|
|
|
// dds files will be uncompressed on a render. requires minimal of info for set this |
|
image.rgba = Mem_Malloc( host.imagepool, image.size ); |
|
memcpy( image.rgba, fin, image.size ); |
|
SetBits( image.flags, IMAGE_DDS_FORMAT ); |
|
|
|
return true; |
|
}
|
|
|