mirror of
https://github.com/w23/xash3d-fwgs
synced 2024-12-17 23:09:40 +01:00
5e0a0765ce
The `.editorconfig` file in this repo is configured to trim all trailing whitespace regardless of whether the line is modified. Trims all trailing whitespace in the repository to make the codebase easier to work with in editors that respect `.editorconfig`. `git blame` becomes less useful on these lines but it already isn't very useful. Commands: ``` find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ ```
347 lines
8.2 KiB
C
347 lines
8.2 KiB
C
/*
|
|
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 )
|
|
{
|
|
uint bits = hdr->dsPixelFormat.dwRGBBitCount;
|
|
|
|
if( !FBitSet( hdr->dsCaps.dwCaps2, DDS_VOLUME ))
|
|
hdr->dwDepth = 1;
|
|
|
|
if( FBitSet( hdr->dsPixelFormat.dwFlags, DDS_FOURCC ))
|
|
{
|
|
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_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;
|
|
|
|
if( filesize < sizeof( dds_t ))
|
|
return false;
|
|
|
|
memcpy( &header, buffer, sizeof( dds_t ));
|
|
|
|
if( header.dwIdent != DDSHEADER )
|
|
return false; // it's not a dds file, just skip it
|
|
|
|
if( header.dwSize != sizeof( dds_t ) - 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;
|
|
}
|
|
|
|
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 ); // 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 - 128 );
|
|
if( image.size == 0 ) return false; // just in case
|
|
fin = (byte *)(buffer + sizeof( dds_t ));
|
|
|
|
// 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 );
|
|
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;
|
|
}
|