xash3d-fwgs/engine/common/imagelib/img_dds.c
Gleb Mazovetskiy 5e0a0765ce Trim all trailing whitespace
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:]]\+$//' {} \+
```
2021-01-04 20:55:10 +03:00

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;
}