#include "CImageLoaderDDS.h"
#ifdef _IRR_COMPILE_WITH_DDS_LOADER_
#include "IReadFile.h"
#include "os.h"
#include "CColorConverter.h"
#include "CImage.h"
#include "irrString.h"
namespace irr
{
namespace video
{
void DDSDecodePixelFormat( ddsBuffer *dds, eDDSPixelFormat *pf )
{
u32 fourCC;
if( dds == NULL || pf == NULL )
return;
fourCC = dds->pixelFormat.fourCC;
if( fourCC == 0 )
*pf = DDS_PF_ARGB8888;
else if( fourCC == *((u32*) "DXT1") )
*pf = DDS_PF_DXT1;
else if( fourCC == *((u32*) "DXT2") )
*pf = DDS_PF_DXT2;
else if( fourCC == *((u32*) "DXT3") )
*pf = DDS_PF_DXT3;
else if( fourCC == *((u32*) "DXT4") )
*pf = DDS_PF_DXT4;
else if( fourCC == *((u32*) "DXT5") )
*pf = DDS_PF_DXT5;
else
*pf = DDS_PF_UNKNOWN;
}
s32 DDSGetInfo( ddsBuffer *dds, s32 *width, s32 *height, eDDSPixelFormat *pf )
{
if( dds == NULL )
return -1;
if( *((s32*) dds->magic) != *((s32*) "DDS ") )
return -1;
if( DDSLittleLong( dds->size ) != 124 )
return -1;
if( width != NULL )
*width = DDSLittleLong( dds->width );
if( height != NULL )
*height = DDSLittleLong( dds->height );
DDSDecodePixelFormat( dds, pf );
return 0;
}
void DDSGetColorBlockColors( ddsColorBlock *block, ddsColor colors[ 4 ] )
{
u16 word;
word = DDSLittleShort( block->colors[ 0 ] );
colors[ 0 ].a = 0xff;
colors[ 0 ].b = (u8) word;
colors[ 0 ].b <<= 3;
colors[ 0 ].b |= (colors[ 0 ].b >> 5);
word >>= 5;
colors[ 0 ].g = (u8) word;
colors[ 0 ].g <<= 2;
colors[ 0 ].g |= (colors[ 0 ].g >> 5);
word >>= 6;
colors[ 0 ].r = (u8) word;
colors[ 0 ].r <<= 3;
colors[ 0 ].r |= (colors[ 0 ].r >> 5);
word = DDSLittleShort( block->colors[ 1 ] );
colors[ 1 ].a = 0xff;
colors[ 1 ].b = (u8) word;
colors[ 1 ].b <<= 3;
colors[ 1 ].b |= (colors[ 1 ].b >> 5);
word >>= 5;
colors[ 1 ].g = (u8) word;
colors[ 1 ].g <<= 2;
colors[ 1 ].g |= (colors[ 1 ].g >> 5);
word >>= 6;
colors[ 1 ].r = (u8) word;
colors[ 1 ].r <<= 3;
colors[ 1 ].r |= (colors[ 1 ].r >> 5);
if( block->colors[ 0 ] > block->colors[ 1 ] )
{
word = ((u16) colors[ 0 ].r * 2 + (u16) colors[ 1 ].r ) / 3;
colors[ 2 ].r = (u8) word;
word = ((u16) colors[ 0 ].g * 2 + (u16) colors[ 1 ].g) / 3;
colors[ 2 ].g = (u8) word;
word = ((u16) colors[ 0 ].b * 2 + (u16) colors[ 1 ].b) / 3;
colors[ 2 ].b = (u8) word;
colors[ 2 ].a = 0xff;
word = ((u16) colors[ 0 ].r + (u16) colors[ 1 ].r * 2) / 3;
colors[ 3 ].r = (u8) word;
word = ((u16) colors[ 0 ].g + (u16) colors[ 1 ].g * 2) / 3;
colors[ 3 ].g = (u8) word;
word = ((u16) colors[ 0 ].b + (u16) colors[ 1 ].b * 2) / 3;
colors[ 3 ].b = (u8) word;
colors[ 3 ].a = 0xff;
}
else
{
word = ((u16) colors[ 0 ].r + (u16) colors[ 1 ].r) / 2;
colors[ 2 ].r = (u8) word;
word = ((u16) colors[ 0 ].g + (u16) colors[ 1 ].g) / 2;
colors[ 2 ].g = (u8) word;
word = ((u16) colors[ 0 ].b + (u16) colors[ 1 ].b) / 2;
colors[ 2 ].b = (u8) word;
colors[ 2 ].a = 0xff;
colors[ 3 ].r = 0x00;
colors[ 3 ].g = 0xff;
colors[ 3 ].b = 0xff;
colors[ 3 ].a = 0x00;
}
}
static void DDSDecodeColorBlock( u32 *pixel, ddsColorBlock *block, s32 width, u32 colors[ 4 ] )
{
s32 r, n;
u32 bits;
u32 masks[] = { 3, 12, 3 << 4, 3 << 6 };
s32 shift[] = { 0, 2, 4, 6 };
for( r = 0; r < 4; r++, pixel += (width - 4) )
{
for( n = 0; n < 4; n++ )
{
bits = block->row[ r ] & masks[ n ];
bits >>= shift[ n ];
switch( bits )
{
case 0:
*pixel = colors[ 0 ];
pixel++;
break;
case 1:
*pixel = colors[ 1 ];
pixel++;
break;
case 2:
*pixel = colors[ 2 ];
pixel++;
break;
case 3:
*pixel = colors[ 3 ];
pixel++;
break;
default:
pixel++;
break;
}
}
}
}
static void DDSDecodeAlphaExplicit( u32 *pixel, ddsAlphaBlockExplicit *alphaBlock, s32 width, u32 alphaZero )
{
s32 row, pix;
u16 word;
ddsColor color;
color.r = 0;
color.g = 0;
color.b = 0;
for( row = 0; row < 4; row++, pixel += (width - 4) )
{
word = DDSLittleShort( alphaBlock->row[ row ] );
for( pix = 0; pix < 4; pix++ )
{
*pixel &= alphaZero;
color.a = word & 0x000F;
color.a = color.a | (color.a << 4);
*pixel |= *((u32*) &color);
word >>= 4;
pixel++;
}
}
}
static void DDSDecodeAlpha3BitLinear( u32 *pixel, ddsAlphaBlock3BitLinear *alphaBlock, s32 width, u32 alphaZero )
{
s32 row, pix;
u32 stuff;
u8 bits[ 4 ][ 4 ];
u16 alphas[ 8 ];
ddsColor aColors[ 4 ][ 4 ];
alphas[ 0 ] = alphaBlock->alpha0;
alphas[ 1 ] = alphaBlock->alpha1;
if( alphas[ 0 ] > alphas[ 1 ] )
{
alphas[ 2 ] = ( 6 * alphas[ 0 ] + alphas[ 1 ]) / 7;
alphas[ 3 ] = ( 5 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 7;
alphas[ 4 ] = ( 4 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 7;
alphas[ 5 ] = ( 3 * alphas[ 0 ] + 4 * alphas[ 1 ]) / 7;
alphas[ 6 ] = ( 2 * alphas[ 0 ] + 5 * alphas[ 1 ]) / 7;
alphas[ 7 ] = ( alphas[ 0 ] + 6 * alphas[ 1 ]) / 7;
}
else
{
alphas[ 2 ] = (4 * alphas[ 0 ] + alphas[ 1 ]) / 5;
alphas[ 3 ] = (3 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 5;
alphas[ 4 ] = (2 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 5;
alphas[ 5 ] = ( alphas[ 0 ] + 4 * alphas[ 1 ]) / 5;
alphas[ 6 ] = 0;
alphas[ 7 ] = 255;
}
stuff = *((u32*) &(alphaBlock->stuff[ 0 ]));
bits[ 0 ][ 0 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 0 ][ 1 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 0 ][ 2 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 0 ][ 3 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 1 ][ 0 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 1 ][ 1 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 1 ][ 2 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 1 ][ 3 ] = (u8) (stuff & 0x00000007);
stuff = *((u32*) &(alphaBlock->stuff[ 3 ]));
bits[ 2 ][ 0 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 2 ][ 1 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 2 ][ 2 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 2 ][ 3 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 3 ][ 0 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 3 ][ 1 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 3 ][ 2 ] = (u8) (stuff & 0x00000007);
stuff >>= 3;
bits[ 3 ][ 3 ] = (u8) (stuff & 0x00000007);
for( row = 0; row < 4; row++ )
{
for( pix=0; pix < 4; pix++ )
{
aColors[ row ][ pix ].r = 0;
aColors[ row ][ pix ].g = 0;
aColors[ row ][ pix ].b = 0;
aColors[ row ][ pix ].a = (u8) alphas[ bits[ row ][ pix ] ];
}
}
for( row = 0; row < 4; row++, pixel += width-4 )
{
for( pix = 0; pix < 4; pix++ )
{
*pixel &= alphaZero;
*pixel |= *((u32*) &(aColors[ row ][ pix ]));
pixel++;
}
}
}
s32 DDSDecompressDXT1( ddsBuffer *dds, s32 width, s32 height, u8 *pixels )
{
s32 x, y, xBlocks, yBlocks;
u32 *pixel;
ddsColorBlock *block;
ddsColor colors[ 4 ];
xBlocks = width / 4;
yBlocks = height / 4;
for( y = 0; y < yBlocks; y++ )
{
block = (ddsColorBlock*) ((u32) dds->data + y * xBlocks * 8);
for( x = 0; x < xBlocks; x++, block++ )
{
DDSGetColorBlockColors( block, colors );
pixel = (u32*) (pixels + x * 16 + (y * 4) * width * 4);
DDSDecodeColorBlock( pixel, block, width, (u32*) colors );
}
}
return 0;
}
s32 DDSDecompressDXT3( ddsBuffer *dds, s32 width, s32 height, u8 *pixels )
{
s32 x, y, xBlocks, yBlocks;
u32 *pixel, alphaZero;
ddsColorBlock *block;
ddsAlphaBlockExplicit *alphaBlock;
ddsColor colors[ 4 ];
xBlocks = width / 4;
yBlocks = height / 4;
colors[ 0 ].a = 0;
colors[ 0 ].r = 0xFF;
colors[ 0 ].g = 0xFF;
colors[ 0 ].b = 0xFF;
alphaZero = *((u32*) &colors[ 0 ]);
for( y = 0; y < yBlocks; y++ )
{
block = (ddsColorBlock*) ((u32) dds->data + y * xBlocks * 16);
for( x = 0; x < xBlocks; x++, block++ )
{
alphaBlock = (ddsAlphaBlockExplicit*) block;
block++;
DDSGetColorBlockColors( block, colors );
pixel = (u32*) (pixels + x * 16 + (y * 4) * width * 4);
DDSDecodeColorBlock( pixel, block, width, (u32*) colors );
DDSDecodeAlphaExplicit( pixel, alphaBlock, width, alphaZero );
}
}
return 0;
}
s32 DDSDecompressDXT5( ddsBuffer *dds, s32 width, s32 height, u8 *pixels )
{
s32 x, y, xBlocks, yBlocks;
u32 *pixel, alphaZero;
ddsColorBlock *block;
ddsAlphaBlock3BitLinear *alphaBlock;
ddsColor colors[ 4 ];
xBlocks = width / 4;
yBlocks = height / 4;
colors[ 0 ].a = 0;
colors[ 0 ].r = 0xFF;
colors[ 0 ].g = 0xFF;
colors[ 0 ].b = 0xFF;
alphaZero = *((u32*) &colors[ 0 ]);
for( y = 0; y < yBlocks; y++ )
{
block = (ddsColorBlock*) ((u32) dds->data + y * xBlocks * 16);
for( x = 0; x < xBlocks; x++, block++ )
{
alphaBlock = (ddsAlphaBlock3BitLinear*) block;
block++;
DDSGetColorBlockColors( block, colors );
pixel = (u32*) (pixels + x * 16 + (y * 4) * width * 4);
DDSDecodeColorBlock( pixel, block, width, (u32*) colors );
DDSDecodeAlpha3BitLinear( pixel, alphaBlock, width, alphaZero );
}
}
return 0;
}
s32 DDSDecompressDXT2( ddsBuffer *dds, s32 width, s32 height, u8 *pixels )
{
s32 r;
r = DDSDecompressDXT3( dds, width, height, pixels );
return r;
}
s32 DDSDecompressDXT4( ddsBuffer *dds, s32 width, s32 height, u8 *pixels )
{
s32 r;
r = DDSDecompressDXT5( dds, width, height, pixels );
return r;
}
s32 DDSDecompressARGB8888( ddsBuffer *dds, s32 width, s32 height, u8 *pixels )
{
s32 x, y;
u8 *in, *out;
in = dds->data;
out = pixels;
for( y = 0; y < height; y++ )
{
for( x = 0; x < width; x++ )
{
*out++ = *in++;
*out++ = *in++;
*out++ = *in++;
*out++ = *in++;
}
}
return 0;
}
s32 DDSDecompress( ddsBuffer *dds, u8 *pixels )
{
s32 width, height, r;
eDDSPixelFormat pf;
r = DDSGetInfo( dds, &width, &height, &pf );
if( r )
return r;
switch( pf )
{
case DDS_PF_ARGB8888:
r = DDSDecompressARGB8888( dds, width, height, pixels );
break;
case DDS_PF_DXT1:
r = DDSDecompressDXT1( dds, width, height, pixels );
break;
case DDS_PF_DXT2:
r = DDSDecompressDXT2( dds, width, height, pixels );
break;
case DDS_PF_DXT3:
r = DDSDecompressDXT3( dds, width, height, pixels );
break;
case DDS_PF_DXT4:
r = DDSDecompressDXT4( dds, width, height, pixels );
break;
case DDS_PF_DXT5:
r = DDSDecompressDXT5( dds, width, height, pixels );
break;
default:
case DDS_PF_UNKNOWN:
memset( pixels, 0xFF, width * height * 4 );
r = -1;
break;
}
return r;
}
bool CImageLoaderDDS::isALoadableFileExtension(const io::path& filename) const
{
return core::hasFileExtension ( filename, "dds" );
}
bool CImageLoaderDDS::isALoadableFileFormat(io::IReadFile* file) const
{
if (!file)
return false;
ddsBuffer header;
file->read(&header, sizeof(header));
s32 width, height;
eDDSPixelFormat pixelFormat;
return 0 == DDSGetInfo( &header, &width, &height, &pixelFormat);
}
IImage* CImageLoaderDDS::loadImage(io::IReadFile* file) const
{
u8 *memFile = new u8 [ file->getSize() ];
file->read ( memFile, file->getSize() );
ddsBuffer *header = (ddsBuffer*) memFile;
IImage* image = 0;
s32 width, height;
eDDSPixelFormat pixelFormat;
if ( 0 == DDSGetInfo( header, &width, &height, &pixelFormat) )
{
image = new CImage(ECF_A8R8G8B8, core::dimension2d<u32>(width, height));
if ( DDSDecompress( header, (u8*) image->lock() ) == -1)
{
image->unlock();
image->drop();
image = 0;
}
}
delete [] memFile;
if ( image )
image->unlock();
return image;
}
IImageLoader* createImageLoaderDDS()
{
return new CImageLoaderDDS();
}
}
}
#endif