mirror of
https://github.com/minetest/irrlicht.git
synced 2024-11-14 06:20:26 +01:00
888 lines
21 KiB
C++
888 lines
21 KiB
C++
|
// Copyright (C) 2013 Patryk Nadrowski
|
||
|
// Heavily based on the DDS loader implemented by Thomas Alten
|
||
|
// and DDS loader from IrrSpintz implemented by Thomas Ince
|
||
|
// This file is part of the "Irrlicht Engine".
|
||
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||
|
|
||
|
/*
|
||
|
Based on Code from Copyright (c) 2003 Randy Reddig
|
||
|
Based on code from Nvidia's DDS example:
|
||
|
http://www.nvidia.com/object/dxtc_decompression_code.html
|
||
|
|
||
|
mainly c to cpp
|
||
|
*/
|
||
|
|
||
|
#include "CImageLoaderDDS.h"
|
||
|
|
||
|
#if defined(_IRR_COMPILE_WITH_DDS_LOADER_) || defined(_IRR_COMPILE_WITH_DDS_DECODER_LOADER_)
|
||
|
|
||
|
#include "IReadFile.h"
|
||
|
#include "os.h"
|
||
|
#include "CColorConverter.h"
|
||
|
#include "CImage.h"
|
||
|
#include "irrString.h"
|
||
|
|
||
|
// Header flag values
|
||
|
#define DDSD_CAPS 0x00000001
|
||
|
#define DDSD_HEIGHT 0x00000002
|
||
|
#define DDSD_WIDTH 0x00000004
|
||
|
#define DDSD_PITCH 0x00000008
|
||
|
#define DDSD_PIXELFORMAT 0x00001000
|
||
|
#define DDSD_MIPMAPCOUNT 0x00020000
|
||
|
#define DDSD_LINEARSIZE 0x00080000
|
||
|
#define DDSD_DEPTH 0x00800000
|
||
|
|
||
|
// Pixel format flag values
|
||
|
#define DDPF_ALPHAPIXELS 0x00000001
|
||
|
#define DDPF_ALPHA 0x00000002
|
||
|
#define DDPF_FOURCC 0x00000004
|
||
|
#define DDPF_RGB 0x00000040
|
||
|
#define DDPF_COMPRESSED 0x00000080
|
||
|
#define DDPF_LUMINANCE 0x00020000
|
||
|
|
||
|
// Caps1 values
|
||
|
#define DDSCAPS1_COMPLEX 0x00000008
|
||
|
#define DDSCAPS1_TEXTURE 0x00001000
|
||
|
#define DDSCAPS1_MIPMAP 0x00400000
|
||
|
|
||
|
// Caps2 values
|
||
|
#define DDSCAPS2_CUBEMAP 0x00000200
|
||
|
#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
|
||
|
#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
|
||
|
#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
|
||
|
#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
|
||
|
#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
|
||
|
#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000
|
||
|
#define DDSCAPS2_VOLUME 0x00200000
|
||
|
|
||
|
namespace irr
|
||
|
{
|
||
|
|
||
|
namespace video
|
||
|
{
|
||
|
|
||
|
/*
|
||
|
DDSGetInfo()
|
||
|
extracts relevant info from a dds texture, returns 0 on success
|
||
|
*/
|
||
|
s32 DDSGetInfo(ddsHeader* dds, s32* width, s32* height, eDDSPixelFormat* pf)
|
||
|
{
|
||
|
/* dummy test */
|
||
|
if( dds == NULL )
|
||
|
return -1;
|
||
|
|
||
|
/* test dds header */
|
||
|
if( *((s32*) dds->Magic) != *((s32*) "DDS ") )
|
||
|
return -1;
|
||
|
if( DDSLittleLong( dds->Size ) != 124 )
|
||
|
return -1;
|
||
|
if( !(DDSLittleLong( dds->Flags ) & DDSD_PIXELFORMAT) )
|
||
|
return -1;
|
||
|
if( !(DDSLittleLong( dds->Flags ) & DDSD_CAPS) )
|
||
|
return -1;
|
||
|
|
||
|
/* extract width and height */
|
||
|
if( width != NULL )
|
||
|
*width = DDSLittleLong( dds->Width );
|
||
|
if( height != NULL )
|
||
|
*height = DDSLittleLong( dds->Height );
|
||
|
|
||
|
/* get pixel format */
|
||
|
|
||
|
/* extract fourCC */
|
||
|
const u32 fourCC = dds->PixelFormat.FourCC;
|
||
|
|
||
|
/* test it */
|
||
|
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;
|
||
|
|
||
|
/* return ok */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef _IRR_COMPILE_WITH_DDS_DECODER_LOADER_
|
||
|
|
||
|
/*
|
||
|
DDSDecompressARGB8888()
|
||
|
decompresses an argb 8888 format texture
|
||
|
*/
|
||
|
s32 DDSDecompressARGB8888(ddsHeader* dds, u8* data, s32 width, s32 height, u8* pixels)
|
||
|
{
|
||
|
/* setup */
|
||
|
u8* in = data;
|
||
|
u8* out = pixels;
|
||
|
|
||
|
/* walk y */
|
||
|
for(s32 y = 0; y < height; y++)
|
||
|
{
|
||
|
/* walk x */
|
||
|
for(s32 x = 0; x < width; x++)
|
||
|
{
|
||
|
*out++ = *in++;
|
||
|
*out++ = *in++;
|
||
|
*out++ = *in++;
|
||
|
*out++ = *in++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* return ok */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
DDSGetColorBlockColors()
|
||
|
extracts colors from a dds color block
|
||
|
*/
|
||
|
void DDSGetColorBlockColors(ddsColorBlock* block, ddsColor colors[4])
|
||
|
{
|
||
|
u16 word;
|
||
|
|
||
|
|
||
|
/* color 0 */
|
||
|
word = DDSLittleShort( block->colors[ 0 ] );
|
||
|
colors[ 0 ].a = 0xff;
|
||
|
|
||
|
/* extract rgb bits */
|
||
|
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);
|
||
|
|
||
|
/* same for color 1 */
|
||
|
word = DDSLittleShort( block->colors[ 1 ] );
|
||
|
colors[ 1 ].a = 0xff;
|
||
|
|
||
|
/* extract rgb bits */
|
||
|
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);
|
||
|
|
||
|
/* use this for all but the super-freak math method */
|
||
|
if( block->colors[ 0 ] > block->colors[ 1 ] )
|
||
|
{
|
||
|
/* four-color block: derive the other two colors.
|
||
|
00 = color 0, 01 = color 1, 10 = color 2, 11 = color 3
|
||
|
these two bit codes correspond to the 2-bit fields
|
||
|
stored in the 64-bit block. */
|
||
|
|
||
|
word = ((u16) colors[ 0 ].r * 2 + (u16) colors[ 1 ].r ) / 3;
|
||
|
/* no +1 for rounding */
|
||
|
/* as bits have been shifted to 888 */
|
||
|
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
|
||
|
{
|
||
|
/* three-color block: derive the other color.
|
||
|
00 = color 0, 01 = color 1, 10 = color 2,
|
||
|
11 = transparent.
|
||
|
These two bit codes correspond to the 2-bit fields
|
||
|
stored in the 64-bit block */
|
||
|
|
||
|
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;
|
||
|
|
||
|
/* random color to indicate alpha */
|
||
|
colors[ 3 ].r = 0x00;
|
||
|
colors[ 3 ].g = 0xff;
|
||
|
colors[ 3 ].b = 0xff;
|
||
|
colors[ 3 ].a = 0x00;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
DDSDecodeColorBlock()
|
||
|
decodes a dds color block
|
||
|
fixme: make endian-safe
|
||
|
*/
|
||
|
|
||
|
void DDSDecodeColorBlock(u32* pixel, ddsColorBlock* block, s32 width, u32 colors[4])
|
||
|
{
|
||
|
s32 r, n;
|
||
|
u32 bits;
|
||
|
u32 masks[] = { 3, 12, 3 << 4, 3 << 6 }; /* bit masks = 00000011, 00001100, 00110000, 11000000 */
|
||
|
s32 shift[] = { 0, 2, 4, 6 };
|
||
|
|
||
|
|
||
|
/* r steps through lines in y */
|
||
|
for( r = 0; r < 4; r++, pixel += (width - 4) ) /* no width * 4 as u32 ptr inc will * 4 */
|
||
|
{
|
||
|
/* width * 4 bytes per pixel per line, each j dxtc row is 4 lines of pixels */
|
||
|
|
||
|
/* n steps through pixels */
|
||
|
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:
|
||
|
/* invalid */
|
||
|
pixel++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
DDSDecodeAlphaExplicit()
|
||
|
decodes a dds explicit alpha block
|
||
|
*/
|
||
|
void DDSDecodeAlphaExplicit(u32* pixel, ddsAlphaBlockExplicit* alphaBlock, s32 width, u32 alphaZero)
|
||
|
{
|
||
|
s32 row, pix;
|
||
|
u16 word;
|
||
|
ddsColor color;
|
||
|
|
||
|
|
||
|
/* clear color */
|
||
|
color.r = 0;
|
||
|
color.g = 0;
|
||
|
color.b = 0;
|
||
|
|
||
|
/* walk rows */
|
||
|
for( row = 0; row < 4; row++, pixel += (width - 4) )
|
||
|
{
|
||
|
word = DDSLittleShort( alphaBlock->row[ row ] );
|
||
|
|
||
|
/* walk pixels */
|
||
|
for( pix = 0; pix < 4; pix++ )
|
||
|
{
|
||
|
/* zero the alpha bits of image pixel */
|
||
|
*pixel &= alphaZero;
|
||
|
color.a = word & 0x000F;
|
||
|
color.a = color.a | (color.a << 4);
|
||
|
*pixel |= *((u32*) &color);
|
||
|
word >>= 4; /* move next bits to lowest 4 */
|
||
|
pixel++; /* move to next pixel in the row */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
DDSDecodeAlpha3BitLinear()
|
||
|
decodes interpolated alpha block
|
||
|
*/
|
||
|
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 ];
|
||
|
|
||
|
/* get initial alphas */
|
||
|
alphas[ 0 ] = alphaBlock->alpha0;
|
||
|
alphas[ 1 ] = alphaBlock->alpha1;
|
||
|
|
||
|
/* 8-alpha block */
|
||
|
if( alphas[ 0 ] > alphas[ 1 ] )
|
||
|
{
|
||
|
/* 000 = alpha_0, 001 = alpha_1, others are interpolated */
|
||
|
alphas[ 2 ] = ( 6 * alphas[ 0 ] + alphas[ 1 ]) / 7; /* bit code 010 */
|
||
|
alphas[ 3 ] = ( 5 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 7; /* bit code 011 */
|
||
|
alphas[ 4 ] = ( 4 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 7; /* bit code 100 */
|
||
|
alphas[ 5 ] = ( 3 * alphas[ 0 ] + 4 * alphas[ 1 ]) / 7; /* bit code 101 */
|
||
|
alphas[ 6 ] = ( 2 * alphas[ 0 ] + 5 * alphas[ 1 ]) / 7; /* bit code 110 */
|
||
|
alphas[ 7 ] = ( alphas[ 0 ] + 6 * alphas[ 1 ]) / 7; /* bit code 111 */
|
||
|
}
|
||
|
|
||
|
/* 6-alpha block */
|
||
|
else
|
||
|
{
|
||
|
/* 000 = alpha_0, 001 = alpha_1, others are interpolated */
|
||
|
alphas[ 2 ] = (4 * alphas[ 0 ] + alphas[ 1 ]) / 5; /* bit code 010 */
|
||
|
alphas[ 3 ] = (3 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 5; /* bit code 011 */
|
||
|
alphas[ 4 ] = (2 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 5; /* bit code 100 */
|
||
|
alphas[ 5 ] = ( alphas[ 0 ] + 4 * alphas[ 1 ]) / 5; /* bit code 101 */
|
||
|
alphas[ 6 ] = 0; /* bit code 110 */
|
||
|
alphas[ 7 ] = 255; /* bit code 111 */
|
||
|
}
|
||
|
|
||
|
/* decode 3-bit fields into array of 16 bytes with same value */
|
||
|
|
||
|
/* first two rows of 4 pixels each */
|
||
|
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);
|
||
|
|
||
|
/* last two rows */
|
||
|
stuff = *((u32*) &(alphaBlock->stuff[ 3 ])); /* last 3 bytes */
|
||
|
|
||
|
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);
|
||
|
|
||
|
/* decode the codes into alpha values */
|
||
|
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 ] ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* write out alpha values to the image bits */
|
||
|
for( row = 0; row < 4; row++, pixel += width-4 )
|
||
|
{
|
||
|
for( pix = 0; pix < 4; pix++ )
|
||
|
{
|
||
|
/* zero the alpha bits of image pixel */
|
||
|
*pixel &= alphaZero;
|
||
|
|
||
|
/* or the bits into the prev. nulled alpha */
|
||
|
*pixel |= *((u32*) &(aColors[ row ][ pix ]));
|
||
|
pixel++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
DDSDecompressDXT1()
|
||
|
decompresses a dxt1 format texture
|
||
|
*/
|
||
|
s32 DDSDecompressDXT1(ddsHeader* dds, u8* data, s32 width, s32 height, u8* pixels)
|
||
|
{
|
||
|
s32 x, y, xBlocks, yBlocks;
|
||
|
u32 *pixel;
|
||
|
ddsColorBlock *block;
|
||
|
ddsColor colors[ 4 ];
|
||
|
|
||
|
/* setup */
|
||
|
xBlocks = width / 4;
|
||
|
yBlocks = height / 4;
|
||
|
|
||
|
/* walk y */
|
||
|
for( y = 0; y < yBlocks; y++ )
|
||
|
{
|
||
|
/* 8 bytes per block */
|
||
|
block = (ddsColorBlock*) (data + y * xBlocks * 8);
|
||
|
|
||
|
/* walk x */
|
||
|
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 ok */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
DDSDecompressDXT3()
|
||
|
decompresses a dxt3 format texture
|
||
|
*/
|
||
|
|
||
|
s32 DDSDecompressDXT3(ddsHeader* dds, u8* data, s32 width, s32 height, u8* pixels)
|
||
|
{
|
||
|
s32 x, y, xBlocks, yBlocks;
|
||
|
u32 *pixel, alphaZero;
|
||
|
ddsColorBlock *block;
|
||
|
ddsAlphaBlockExplicit *alphaBlock;
|
||
|
ddsColor colors[ 4 ];
|
||
|
|
||
|
/* setup */
|
||
|
xBlocks = width / 4;
|
||
|
yBlocks = height / 4;
|
||
|
|
||
|
/* create zero alpha */
|
||
|
colors[ 0 ].a = 0;
|
||
|
colors[ 0 ].r = 0xFF;
|
||
|
colors[ 0 ].g = 0xFF;
|
||
|
colors[ 0 ].b = 0xFF;
|
||
|
alphaZero = *((u32*) &colors[ 0 ]);
|
||
|
|
||
|
/* walk y */
|
||
|
for( y = 0; y < yBlocks; y++ )
|
||
|
{
|
||
|
/* 8 bytes per block, 1 block for alpha, 1 block for color */
|
||
|
block = (ddsColorBlock*) (data + y * xBlocks * 16);
|
||
|
|
||
|
/* walk x */
|
||
|
for( x = 0; x < xBlocks; x++, block++ )
|
||
|
{
|
||
|
/* get alpha block */
|
||
|
alphaBlock = (ddsAlphaBlockExplicit*) block;
|
||
|
|
||
|
/* get color block */
|
||
|
block++;
|
||
|
DDSGetColorBlockColors( block, colors );
|
||
|
|
||
|
/* decode color block */
|
||
|
pixel = (u32*) (pixels + x * 16 + (y * 4) * width * 4);
|
||
|
DDSDecodeColorBlock( pixel, block, width, (u32*) colors );
|
||
|
|
||
|
/* overwrite alpha bits with alpha block */
|
||
|
DDSDecodeAlphaExplicit( pixel, alphaBlock, width, alphaZero );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* return ok */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
DDSDecompressDXT5()
|
||
|
decompresses a dxt5 format texture
|
||
|
*/
|
||
|
s32 DDSDecompressDXT5(ddsHeader* dds, u8* data, s32 width, s32 height, u8* pixels)
|
||
|
{
|
||
|
s32 x, y, xBlocks, yBlocks;
|
||
|
u32 *pixel, alphaZero;
|
||
|
ddsColorBlock *block;
|
||
|
ddsAlphaBlock3BitLinear *alphaBlock;
|
||
|
ddsColor colors[ 4 ];
|
||
|
|
||
|
/* setup */
|
||
|
xBlocks = width / 4;
|
||
|
yBlocks = height / 4;
|
||
|
|
||
|
/* create zero alpha */
|
||
|
colors[ 0 ].a = 0;
|
||
|
colors[ 0 ].r = 0xFF;
|
||
|
colors[ 0 ].g = 0xFF;
|
||
|
colors[ 0 ].b = 0xFF;
|
||
|
alphaZero = *((u32*) &colors[ 0 ]);
|
||
|
|
||
|
/* walk y */
|
||
|
for( y = 0; y < yBlocks; y++ )
|
||
|
{
|
||
|
/* 8 bytes per block, 1 block for alpha, 1 block for color */
|
||
|
block = (ddsColorBlock*) (data + y * xBlocks * 16);
|
||
|
|
||
|
/* walk x */
|
||
|
for( x = 0; x < xBlocks; x++, block++ )
|
||
|
{
|
||
|
/* get alpha block */
|
||
|
alphaBlock = (ddsAlphaBlock3BitLinear*) block;
|
||
|
|
||
|
/* get color block */
|
||
|
block++;
|
||
|
DDSGetColorBlockColors( block, colors );
|
||
|
|
||
|
/* decode color block */
|
||
|
pixel = (u32*) (pixels + x * 16 + (y * 4) * width * 4);
|
||
|
DDSDecodeColorBlock( pixel, block, width, (u32*) colors );
|
||
|
|
||
|
/* overwrite alpha bits with alpha block */
|
||
|
DDSDecodeAlpha3BitLinear( pixel, alphaBlock, width, alphaZero );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* return ok */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
DDSDecompressDXT2()
|
||
|
decompresses a dxt2 format texture (fixme: un-premultiply alpha)
|
||
|
*/
|
||
|
s32 DDSDecompressDXT2(ddsHeader* dds, u8* data, s32 width, s32 height, u8* pixels)
|
||
|
{
|
||
|
/* decompress dxt3 first */
|
||
|
const s32 r = DDSDecompressDXT3( dds, data, width, height, pixels );
|
||
|
|
||
|
/* return to sender */
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
DDSDecompressDXT4()
|
||
|
decompresses a dxt4 format texture (fixme: un-premultiply alpha)
|
||
|
*/
|
||
|
s32 DDSDecompressDXT4(ddsHeader* dds, u8* data, s32 width, s32 height, u8* pixels)
|
||
|
{
|
||
|
/* decompress dxt5 first */
|
||
|
const s32 r = DDSDecompressDXT5( dds, data, width, height, pixels );
|
||
|
|
||
|
/* return to sender */
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
DDSDecompress()
|
||
|
decompresses a dds texture into an rgba image buffer, returns 0 on success
|
||
|
*/
|
||
|
s32 DDSDecompress(ddsHeader* dds, u8* data, u8* pixels)
|
||
|
{
|
||
|
s32 width, height;
|
||
|
eDDSPixelFormat pf;
|
||
|
|
||
|
/* get dds info */
|
||
|
s32 r = DDSGetInfo( dds, &width, &height, &pf );
|
||
|
if ( r )
|
||
|
return r;
|
||
|
|
||
|
/* decompress */
|
||
|
switch( pf )
|
||
|
{
|
||
|
case DDS_PF_ARGB8888:
|
||
|
/* fixme: support other [a]rgb formats */
|
||
|
r = DDSDecompressARGB8888( dds, data, width, height, pixels );
|
||
|
break;
|
||
|
|
||
|
case DDS_PF_DXT1:
|
||
|
r = DDSDecompressDXT1( dds, data, width, height, pixels );
|
||
|
break;
|
||
|
|
||
|
case DDS_PF_DXT2:
|
||
|
r = DDSDecompressDXT2( dds, data, width, height, pixels );
|
||
|
break;
|
||
|
|
||
|
case DDS_PF_DXT3:
|
||
|
r = DDSDecompressDXT3( dds, data, width, height, pixels );
|
||
|
break;
|
||
|
|
||
|
case DDS_PF_DXT4:
|
||
|
r = DDSDecompressDXT4( dds, data, width, height, pixels );
|
||
|
break;
|
||
|
|
||
|
case DDS_PF_DXT5:
|
||
|
r = DDSDecompressDXT5( dds, data, width, height, pixels );
|
||
|
break;
|
||
|
|
||
|
default: // DDS_PF_UNKNOWN
|
||
|
r = -1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* return to sender */
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//! returns true if the file maybe is able to be loaded by this class
|
||
|
//! based on the file extension (e.g. ".tga")
|
||
|
bool CImageLoaderDDS::isALoadableFileExtension(const io::path& filename) const
|
||
|
{
|
||
|
return core::hasFileExtension(filename, "dds");
|
||
|
}
|
||
|
|
||
|
|
||
|
//! returns true if the file maybe is able to be loaded by this class
|
||
|
bool CImageLoaderDDS::isALoadableFileFormat(io::IReadFile* file) const
|
||
|
{
|
||
|
if (!file)
|
||
|
return false;
|
||
|
|
||
|
c8 MagicWord[4];
|
||
|
file->read(&MagicWord, 4);
|
||
|
|
||
|
return (MagicWord[0] == 'D' && MagicWord[1] == 'D' && MagicWord[2] == 'S');
|
||
|
}
|
||
|
|
||
|
|
||
|
//! creates a surface from the file
|
||
|
IImage* CImageLoaderDDS::loadImage(io::IReadFile* file) const
|
||
|
{
|
||
|
ddsHeader header;
|
||
|
IImage* image = 0;
|
||
|
s32 width, height;
|
||
|
eDDSPixelFormat pixelFormat;
|
||
|
ECOLOR_FORMAT format = ECF_UNKNOWN;
|
||
|
u32 dataSize = 0;
|
||
|
u32 mipMapsDataSize = 0;
|
||
|
bool is3D = false;
|
||
|
bool useAlpha = false;
|
||
|
u32 mipMapCount = 0;
|
||
|
|
||
|
file->seek(0);
|
||
|
file->read(&header, sizeof(ddsHeader));
|
||
|
|
||
|
if (0 == DDSGetInfo(&header, &width, &height, &pixelFormat))
|
||
|
{
|
||
|
is3D = header.Depth > 0 && (header.Flags & DDSD_DEPTH);
|
||
|
|
||
|
if (!is3D)
|
||
|
header.Depth = 1;
|
||
|
|
||
|
useAlpha = header.PixelFormat.Flags & DDPF_ALPHAPIXELS;
|
||
|
|
||
|
if (header.MipMapCount > 0 && (header.Flags & DDSD_MIPMAPCOUNT))
|
||
|
mipMapCount = header.MipMapCount;
|
||
|
|
||
|
#ifdef _IRR_COMPILE_WITH_DDS_DECODER_LOADER_
|
||
|
u32 newSize = file->getSize() - sizeof(ddsHeader);
|
||
|
u8* memFile = new u8[newSize];
|
||
|
file->read(memFile, newSize);
|
||
|
|
||
|
image = new CImage(ECF_A8R8G8B8, core::dimension2d<u32>(width, height));
|
||
|
|
||
|
if (DDSDecompress(&header, memFile, (u8*)image->lock()) == -1)
|
||
|
{
|
||
|
image->unlock();
|
||
|
image->drop();
|
||
|
image = 0;
|
||
|
}
|
||
|
|
||
|
delete[] memFile;
|
||
|
#else
|
||
|
if (header.PixelFormat.Flags & DDPF_RGB) // Uncompressed formats
|
||
|
{
|
||
|
// u32 byteCount = header.PixelFormat.RGBBitCount / 8;
|
||
|
|
||
|
if( header.Flags & DDSD_PITCH )
|
||
|
dataSize = header.PitchOrLinearSize * header.Height * header.Depth * (header.PixelFormat.RGBBitCount / 8);
|
||
|
else
|
||
|
dataSize = header.Width * header.Height * header.Depth * (header.PixelFormat.RGBBitCount / 8);
|
||
|
|
||
|
u8* data = new u8[dataSize];
|
||
|
file->read(data, dataSize);
|
||
|
|
||
|
switch (header.PixelFormat.RGBBitCount) // Bytes per pixel
|
||
|
{
|
||
|
case 16:
|
||
|
{
|
||
|
if (useAlpha)
|
||
|
{
|
||
|
if (header.PixelFormat.ABitMask == 0x8000)
|
||
|
format = ECF_A1R5G5B5;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (header.PixelFormat.RBitMask == 0xf800)
|
||
|
format = ECF_R5G6B5;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case 24:
|
||
|
{
|
||
|
if (!useAlpha)
|
||
|
{
|
||
|
if (header.PixelFormat.RBitMask == 0xff0000)
|
||
|
format = ECF_R8G8B8;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case 32:
|
||
|
{
|
||
|
if (useAlpha)
|
||
|
{
|
||
|
if (header.PixelFormat.RBitMask & 0xff0000)
|
||
|
format = ECF_A8R8G8B8;
|
||
|
else if (header.PixelFormat.RBitMask & 0xff)
|
||
|
{
|
||
|
// convert from A8B8G8R8 to A8R8G8B8
|
||
|
u8 tmp = 0;
|
||
|
|
||
|
for (u32 i = 0; i < dataSize; i += 4)
|
||
|
{
|
||
|
tmp = data[i];
|
||
|
data[i] = data[i+2];
|
||
|
data[i+2] = tmp;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (format != ECF_UNKNOWN)
|
||
|
{
|
||
|
if (!is3D) // Currently 3D textures are unsupported.
|
||
|
{
|
||
|
image = new CImage(format, core::dimension2d<u32>(header.Width, header.Height), data, true, true);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
delete[] data;
|
||
|
}
|
||
|
}
|
||
|
else if (header.PixelFormat.Flags & DDPF_FOURCC) // Compressed formats
|
||
|
{
|
||
|
switch(pixelFormat)
|
||
|
{
|
||
|
case DDS_PF_DXT1:
|
||
|
{
|
||
|
format = ECF_DXT1;
|
||
|
break;
|
||
|
}
|
||
|
case DDS_PF_DXT2:
|
||
|
case DDS_PF_DXT3:
|
||
|
{
|
||
|
format = ECF_DXT3;
|
||
|
break;
|
||
|
}
|
||
|
case DDS_PF_DXT4:
|
||
|
case DDS_PF_DXT5:
|
||
|
{
|
||
|
format = ECF_DXT5;
|
||
|
break;
|
||
|
}
|
||
|
default: // either not compressed or unknown
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if( format != ECF_UNKNOWN )
|
||
|
{
|
||
|
if (!is3D) // Currently 3D textures are unsupported.
|
||
|
{
|
||
|
dataSize = IImage::getDataSizeFromFormat(format, header.Width, header.Height);
|
||
|
|
||
|
u8* data = new u8[dataSize];
|
||
|
file->read(data, dataSize);
|
||
|
|
||
|
image = new CImage(format, core::dimension2d<u32>(header.Width, header.Height), data, true, true);
|
||
|
|
||
|
if (mipMapCount > 0)
|
||
|
{
|
||
|
u32 tmpWidth = header.Width;
|
||
|
u32 tmpHeight = header.Height;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if (tmpWidth > 1)
|
||
|
tmpWidth >>= 1;
|
||
|
|
||
|
if (tmpHeight > 1)
|
||
|
tmpHeight >>= 1;
|
||
|
|
||
|
mipMapsDataSize += IImage::getDataSizeFromFormat(format, tmpWidth, tmpHeight);
|
||
|
}
|
||
|
while (tmpWidth != 1 || tmpHeight != 1);
|
||
|
|
||
|
u8* mipMapsData = new u8[mipMapsDataSize];
|
||
|
file->read(mipMapsData, mipMapsDataSize);
|
||
|
|
||
|
image->setMipMapsData(mipMapsData, true, true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return image;
|
||
|
}
|
||
|
|
||
|
|
||
|
//! creates a loader which is able to load dds images
|
||
|
IImageLoader* createImageLoaderDDS()
|
||
|
{
|
||
|
return new CImageLoaderDDS();
|
||
|
}
|
||
|
|
||
|
|
||
|
} // end namespace video
|
||
|
} // end namespace irr
|
||
|
|
||
|
#endif
|
||
|
|