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.

2484 lines
76 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include <memory.h>
#include "hsTypes.h"
#include "hsDXTSoftwareCodec.h"
#include "plMipmap.h"
#include "hsCodecManager.h"
#define SWAPVARS( x, y, t ) { t = x; x = y; y = t; }
// This is the color depth that we decompress to by default if we're not told otherwise
#define kDefaultDepth 32
hsBool hsDXTSoftwareCodec::fRegistered = false;
hsDXTSoftwareCodec& hsDXTSoftwareCodec::Instance()
{
static hsDXTSoftwareCodec the_instance;
return the_instance;
}
hsDXTSoftwareCodec::hsDXTSoftwareCodec()
{
}
hsDXTSoftwareCodec::~hsDXTSoftwareCodec()
{
}
//// CreateCompressedMipmap ///////////////////////////////////////////////////
// Updated 8.15.2000 mcn to generate uncompressed mipmaps down to 1x1 (the
// decompressor better know how to deal with this!) Also cleaned up so I can
// read it :)
plMipmap *hsDXTSoftwareCodec::CreateCompressedMipmap( plMipmap *uncompressed )
{
UInt8 format;
plMipmap *compressed = nil;
UInt8 i;
/// Sanity checks
hsAssert( fRegistered, "Calling member of unregistered codec." );
hsAssert( !uncompressed->IsCompressed(), "Trying to compress already compressed bitmap.");
/// Check width and height
if( ( uncompressed->GetWidth() | uncompressed->GetHeight() ) & 0x03 )
return nil; /// Width and height must be multiple of 4
format = ICalcCompressedFormat( uncompressed );
if( format == plMipmap::DirectXInfo::kError )
return nil;
/// Set up structure
compressed = TRACKED_NEW plMipmap( uncompressed->GetWidth(), uncompressed->GetHeight(), plMipmap::kARGB32Config,
uncompressed->GetNumLevels(), plMipmap::kDirectXCompression, format );
{
/// Now loop through and compress each one (that is a valid size!)
for( i = 0; i < compressed->GetNumLevels(); i++ )
{
uncompressed->SetCurrLevel( i );
compressed->SetCurrLevel( i );
if( ( compressed->GetCurrWidth() | compressed->GetCurrHeight() ) & 0x03 )
break;
CompressMipmapLevel( uncompressed, compressed );
}
/// Now copy the rest straight over
for( ; i < compressed->GetNumLevels(); i++ )
memcpy( compressed->GetLevelPtr( i ), uncompressed->GetLevelPtr( i ), uncompressed->GetLevelSize( i ) );
/// Reset
uncompressed->SetCurrLevel( 0 );
compressed->SetCurrLevel( 0 );
}
return compressed;
}
//// CreateUncompressedBitmap /////////////////////////////////////////////////
//
// Takes a compressed bitmap and uncompresses it. Duh!
//
// 8.14.2000 mcn - Updated to decompress to 16-bit modes and set flags
// accordingly.
// 8.15.2000 mcn - Updated to handle compressed mipmaps with invalid levels
// (i.e. they're not compressed). See CreateCompressedMipmap().
// 8.18.2000 mcn - Updated to handle new flags instead of just a bit depth
plMipmap *hsDXTSoftwareCodec::CreateUncompressedMipmap( plMipmap *compressed, UInt8 flags )
{
plMipmap *uncompressed = nil;
Int32 totalSize;
Int32 width, height;
UInt8 i;
/// Sanity checks
hsAssert( fRegistered, "Calling member of unregistered codec." );
hsAssert( compressed->fCompressionType == plMipmap::kDirectXCompression, "Uncompressing wrong format." );
hsAssert( ( compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1 ) ||
( compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5 ),
"Unsupported directX compression format." );
/// Set up the info
uncompressed = ICreateUncompressedMipmap( compressed, flags );
/// Handle mipmaps?
{
totalSize = 0;
width = compressed->GetWidth();
height = compressed->GetHeight();
for( i = 0; i < compressed->GetNumLevels(); i++ )
{
/// Note: THIS is valid no matter whether the compressed level is
/// really compressed or not
totalSize += width * height * uncompressed->GetPixelSize() >> 3;
width >>= 1;
height >>= 1;
}
/// Loop through and decompress!
width = compressed->GetWidth();
height = compressed->GetHeight();
for( i = 0; i < uncompressed->GetNumLevels(); i++ )
{
uncompressed->SetCurrLevel( i );
compressed->SetCurrLevel( i );
if( ( width | height ) & 0x03 )
break;
UncompressMipmap( uncompressed, compressed, flags );
if( width > 1 )
width >>= 1;
if( height > 1 )
height >>= 1;
}
/// Now loop through the *rest* and just copy (they won't be compressed)
for( ; i < uncompressed->GetNumLevels(); i++ )
{
uncompressed->SetCurrLevel( i );
compressed->SetCurrLevel( i );
IXlateColorBlock( (plMipmap *)uncompressed, (UInt32 *)compressed->GetLevelPtr( i ), flags );
}
/// Reset
uncompressed->SetCurrLevel( 0 );
compressed->SetCurrLevel( 0 );
}
return uncompressed;
}
//// IXlateColorBlock /////////////////////////////////////////////////////////
// Converts a block from ARGB 8888 to the given format of the bitmap.
void hsDXTSoftwareCodec::IXlateColorBlock( plMipmap *destBMap, UInt32 *srcBlock, UInt8 flags )
{
UInt8 *bytePtr;
UInt16 *wordPtr, tempVal;
UInt32 i;
if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB8888 )
memcpy( destBMap->GetCurrLevelPtr(), srcBlock, destBMap->GetCurrLevelSize() );
else
{
if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kAInten88 )
{
/// Upper 8 bits from alpha, lower 8 bits from blue
wordPtr = (UInt16 *)destBMap->GetCurrLevelPtr();
for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ )
{
wordPtr[ i ] = (UInt16)( ( ( srcBlock[ i ] >> 16 ) & 0xff00 ) | ( srcBlock[ i ] & 0x00ff ) );
}
}
else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kInten8 )
{
/// 8 bits from blue
bytePtr = (UInt8 *)destBMap->GetCurrLevelPtr();
for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ )
{
bytePtr[ i ] = (UInt8)( srcBlock[ i ] & 0x00ff );
}
}
else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB1555 )
{
wordPtr = (UInt16 *)destBMap->GetCurrLevelPtr();
if( ( flags & hsCodecManager::kCompOrderMask ) == hsCodecManager::kWeirdCompOrder )
{
/// Really do 5551
for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ )
{
tempVal = (UInt16)( ( ( srcBlock[ i ] & 0x000000f8 ) >> 2 ) |
( ( ( srcBlock[ i ] & 0x0000f800 ) >> 5 ) ) |
( ( ( srcBlock[ i ] & 0x00f80000 ) >> 8 ) ) |
( ( srcBlock[ i ] & 0x80000000 ) >> 31 ) );
wordPtr[ i ] = tempVal;
}
}
else
{
/// Normal 1555
for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ )
{
tempVal = (UInt16)( ( ( srcBlock[ i ] & 0x000000f8 ) >> 3 ) |
( ( ( srcBlock[ i ] & 0x0000f800 ) >> 6 ) ) |
( ( ( srcBlock[ i ] & 0x00f80000 ) >> 9 ) ) |
( ( srcBlock[ i ] & 0x80000000 ) >> 16 ) );
wordPtr[ i ] = tempVal;
}
}
}
else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB4444 )
{
wordPtr = (UInt16 *)destBMap->GetCurrLevelPtr();
if( ( flags & hsCodecManager::kCompOrderMask ) == hsCodecManager::kWeirdCompOrder )
{
/// Really do 4444 reversed (switch red and blue)
for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ )
{
tempVal = (UInt16)( ( ( srcBlock[ i ] & 0x000000f0 ) << 4 ) |
( ( ( srcBlock[ i ] & 0x0000f000 ) >> 8 ) ) |
( ( ( srcBlock[ i ] & 0x00f00000 ) >> 20 ) ) |
( ( ( srcBlock[ i ] & 0xf0000000 ) >> 16 ) ) );
wordPtr[ i ] = tempVal;
}
}
else
{
/// Normal 4444
for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ )
{
tempVal = (UInt16)( ( ( srcBlock[ i ] & 0x000000f0 ) >> 4 ) |
( ( ( srcBlock[ i ] & 0x0000f000 ) >> 8 ) ) |
( ( ( srcBlock[ i ] & 0x00f00000 ) >> 12 ) ) |
( ( ( srcBlock[ i ] & 0xf0000000 ) >> 16 ) ) );
wordPtr[ i ] = tempVal;
}
}
}
else
hsAssert( false, "Unsupported pixel format during color block translation" );
}
}
//// ICreateUncompressedMipmap ////////////////////////////////////////////////
// Sets the fields of an uncompressed bitmap according to the source
// (compressed) bitmap and the given bit depth.
plMipmap *hsDXTSoftwareCodec::ICreateUncompressedMipmap( plMipmap *compressed, UInt8 flags )
{
UInt8 bitDepth, type;
plMipmap *newMap;
if( compressed->GetFlags() & plMipmap::kIntensityMap )
{
/// Intensity map--alpha too?
if( compressed->GetFlags() & plMipmap::kAlphaChannelFlag )
{
type = plMipmap::UncompressedInfo::kAInten88;
bitDepth = 16;
}
else
{
type = plMipmap::UncompressedInfo::kInten8;
bitDepth = 8;
}
}
else
{
/// Default to 32 bit depth
if( ( flags & hsCodecManager::kBitDepthMask ) == hsCodecManager::kDontCareDepth )
bitDepth = kDefaultDepth;
else if( ( flags & hsCodecManager::kBitDepthMask ) == hsCodecManager::k32BitDepth )
bitDepth = 32;
else
bitDepth = 16;
if( bitDepth == 32 )
type = plMipmap::UncompressedInfo::kRGB8888;
else if( compressed->GetFlags() & plMipmap::kAlphaChannelFlag )
type = plMipmap::UncompressedInfo::kRGB4444;
else
type = plMipmap::UncompressedInfo::kRGB1555;
}
newMap = TRACKED_NEW plMipmap( compressed->GetWidth(), compressed->GetHeight(), bitDepth,
compressed->GetNumLevels(), plMipmap::kUncompressed, type );
newMap->SetFlags( compressed->GetFlags() );
return newMap;
}
/*
-----------------------------------------------------------
OLD DECOMPRESSION FUNCTION--KEEPING HERE FOR LEGACY
-----------------------------------------------------------
void hsDXTSoftwareCodec::UncompressMipmap(plMipmap *uncompressed, plMipmap *compressed)
{
UInt32 *compressedImage = (UInt32 *)compressed->GetCurrLevelPtr();
UInt32 *uncompressedImage = (UInt32 *)uncompressed->GetCurrLevelPtr();
UInt32 blockSize = compressed->fDirectXInfo.fBlockSize;
Int32 x, y;
Int32 xMax = compressed->GetCurrWidth() >> 2;
Int32 yMax = compressed->GetCurrHeight() >> 2;
for (x = 0; x < xMax; ++x)
{
for (y = 0; y < yMax; ++y)
{
UInt8 alpha[8];
UInt16 color[4];
UInt32 *block = &compressedImage[(x + xMax * y) * (blockSize >> 2)];
UInt8 *charBlock = (UInt8 *)block;
UInt8 *alphaBlock = nil;
UInt8 *colorBlock = nil;
if (compressed->fDirectXInfo.fCompressionType == hsGBitmap::DirectXInfo::kDXT5)
{
alphaBlock = charBlock;
colorBlock = (charBlock + 8);
alpha[0] = charBlock[0];
alpha[1] = charBlock[1];
// 8-alpha or 6-alpha block?
if (alpha[0] > alpha[1]) {
// 8-alpha block: derive the other 6 alphas.
// 000 = alpha[0], 001 = alpha[1], others are interpolated
alpha[2] = (6 * alpha[0] + alpha[1]) / 7; // bit code 010
alpha[3] = (5 * alpha[0] + 2 * alpha[1]) / 7; // Bit code 011
alpha[4] = (4 * alpha[0] + 3 * alpha[1]) / 7; // Bit code 100
alpha[5] = (3 * alpha[0] + 4 * alpha[1]) / 7; // Bit code 101
alpha[6] = (2 * alpha[0] + 5 * alpha[1]) / 7; // Bit code 110
alpha[7] = (alpha[0] + 6 * alpha[1]) / 7; // Bit code 111
}
else { // 6-alpha block: derive the other alphas.
// 000 = alpha[0], 001 = alpha[1], others are interpolated
alpha[2] = (4 * alpha[0] + alpha[1]) / 5; // Bit code 010
alpha[3] = (3 * alpha[0] + 2 * alpha[1]) / 5; // Bit code 011
alpha[4] = (2 * alpha[0] + 3 * alpha[1]) / 5; // Bit code 100
alpha[5] = (alpha[0] + 4 * alpha[1]) / 5; // Bit code 101
alpha[6] = 0; // Bit code 110
alpha[7] = 255; // Bit code 111
}
}
else if (compressed->fDirectXInfo.fCompressionType == hsGBitmap::DirectXInfo::kDXT1)
{
alphaBlock = nil;
colorBlock = charBlock;
}
else
{
hsAssert(false, "Unrecognized compression scheme.");
}
UInt32 encoding;
color[0] = (colorBlock[1] << 8) | colorBlock[0];
color[1] = (colorBlock[3] << 8) | colorBlock[2];
if (color[0] > color[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.
color[2] = BlendColors16(2, color[0], 1, color[1]);
color[3] = BlendColors16(1, color[0], 2, color[1]);
encoding = kFourColorEncoding;
}
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.
color[2] = BlendColors16(1, color[0], 1, color[1]);
color[3] = 0;
encoding = kThreeColorEncoding;
}
UInt8 r, g, b, a;
Int32 xx, yy;
for (xx = 0; xx < 4; ++xx)
{
for (yy = 0; yy < 4; ++yy)
{
if (alphaBlock)
{
UInt32 alphaMask = 0x7;
UInt32 firstThreeBytes = (alphaBlock[4] << 16) + (alphaBlock[3] << 8) + alphaBlock[2];
UInt32 secondThreeBytes = (alphaBlock[7] << 16) + (alphaBlock[6] << 8) + alphaBlock[5];
UInt32 alphaIndex;
UInt32 alphaShift;
if (yy < 2)
{
alphaShift = 3 * (4 * yy + xx);
alphaIndex = (firstThreeBytes >> alphaShift) & alphaMask;
}
else
{
alphaShift = 3 * (4 * (yy - 2) + xx);
alphaIndex = (secondThreeBytes >> alphaShift) & alphaMask;
}
a = alpha[alphaIndex];
}
else
{
a = 255;
}
UInt32 colorMask = 0x3;
UInt32 colorDWord = (colorBlock[7] << 24) | (colorBlock[6] << 16) |
(colorBlock[5] << 8) | colorBlock[4];
UInt32 colorShift = 2 * (4 * yy + xx);
UInt32 colorIndex = (colorDWord >> colorShift) & colorMask;
if ((encoding == kThreeColorEncoding) && (colorIndex == 3))
{
r = g = b = a = 0;
}
else
{
r = (UInt8)((color[colorIndex] >> 8) & 0xf8);
g = (UInt8)((color[colorIndex] >> 3) & 0xfc);
b = (UInt8)((color[colorIndex] << 3) & 0xf8);
}
hsRGBAColor32* pixel = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx, 4 * y + yy);
pixel->a = a;
pixel->r = r;
pixel->g = g;
pixel->b = b;
}
}
}
}
}
*/
//// UncompressBitmap /////////////////////////////////////////////////////////
//
// Workhorse function distribution. Takes a compressed GBitmapClass as input
// and writes the uncompressed version to the destination bitmap class.
// (Doesn't actually do anything but call the appropriate function per format.
// Change this function to add support for more compression formats.)
//
// Note: Supports DXT1 and DXT5 compression formats.
//
// 7.31.2000 mcn - Created, based on old code (uncredited)
// 8.18.2000 mcn - Updated to handle 'weird' formats and other flags.
void hsDXTSoftwareCodec::UncompressMipmap( plMipmap *destBMap, plMipmap *srcBMap, UInt8 flags )
{
if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB8888 )
{
/// 32-bit ARGB - Can be either DXT5 or DXT1
if( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5 )
IUncompressMipmapDXT5To32( destBMap, srcBMap );
else if( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1 )
IUncompressMipmapDXT1To32( destBMap, srcBMap );
}
else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB1555 )
{
/// 16-bit ARGB 1555--can ONLY be DXT1
hsAssert( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1,
"Only DXT1 bitmaps can decompress to ARGB1555 format!" );
if( ( flags & hsCodecManager::kCompOrderMask ) == hsCodecManager::kWeirdCompOrder )
IUncompressMipmapDXT1To16Weird( destBMap, srcBMap );
else
IUncompressMipmapDXT1To16( destBMap, srcBMap );
}
else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB4444 )
{
/// 16-bit ARGB 4444--can ONLY be DXT5
hsAssert( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5,
"Only DXT5 bitmaps can decompress to ARGB4444 format!" );
if( ( flags & hsCodecManager::kCompOrderMask ) == hsCodecManager::kWeirdCompOrder )
IUncompressMipmapDXT5To16Weird( destBMap, srcBMap );
else
IUncompressMipmapDXT5To16( destBMap, srcBMap );
}
else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kInten8 )
{
/// 8-bit intensity--can ONLY be DXT1
hsAssert( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1,
"Only DXT1 bitmaps can decompress to 8-bit Intensity format!" );
IUncompressMipmapDXT1ToInten( destBMap, srcBMap );
}
else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kAInten88 )
{
/// 16-bit alpha-intensity--can ONLY be DXT5
hsAssert( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5,
"Only DXT5 bitmaps can decompress to 8-8 Alpha-Intensity format!" );
IUncompressMipmapDXT5ToAInten( destBMap, srcBMap );
}
else
hsAssert( false, "Unsupported target decompression format" );
}
//// IUncompressMipmapDXT5To16 ////////////////////////////////////////////////
//
// UncompressBitmap internal call for DXT5 compression. DXT5 is 3-bit linear
// interpolated alpha channel compression. Output is a 16-bit RGB 4444 bitmap.
void hsDXTSoftwareCodec::IUncompressMipmapDXT5To16( plMipmap *destBMap, plMipmap *srcBMap )
{
UInt16 *srcData;
UInt16 *destData, destBlock[ 16 ];
UInt32 blockSize;
UInt32 x, y, bMapStride;
UInt16 colors[ 4 ];
Int32 numBlocks, i, j;
UInt8 *bytePtr;
UInt16 alphas[ 8 ], aTemp, a0, a1;
UInt32 aBitSrc1, aBitSrc2;
UInt16 cBitSrc1, cBitSrc2;
/// Setup some nifty stuff
hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" );
hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" );
numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4;
blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words
srcData = (UInt16 *)srcBMap->GetCurrLevelPtr();
// Note our trick here to make sure nothing breaks if GetAddr16's
// formula changes
bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) );
x = y = 0;
/// Loop through the # of blocks (width*height / 16-pixel-blocks)
for( i = 0; i < numBlocks; i++ )
{
/// Per block--determine alpha compression type first
bytePtr = (UInt8 *)srcData;
alphas[ 0 ] = bytePtr[ 0 ];
alphas[ 1 ] = bytePtr[ 1 ];
/// Note that we use the preshifted alphas really as fixed point.
/// The result: more accuracy, and no need to shift the alphas afterwards
if( alphas[ 0 ] > alphas[ 1 ] )
{
/// 8-alpha block: interpolate 6 others
alphas[ 0 ] <<= 8;
alphas[ 1 ] <<= 8;
/// Note that, unlike below, we can't combine a0 and a1 into
/// one value, because that would give us a negative value,
/// and we're using unsigned values here. (i.e. we need all the bits)
aTemp = alphas[ 0 ];
a0 = ( aTemp / 7 );
a1 = ( alphas[ 1 ] / 7 );
for( j = 2; j < 8; j++ )
{
aTemp += a1 - a0;
alphas[ j ] = aTemp & 0xf000; /// Mask done here to retain as
/// much accuracy as possible
}
}
else
{
/// 6-alpha block: interpolate 4 others, then assume last 2 are 0 and 255
alphas[ 0 ] <<= 8;
alphas[ 1 ] <<= 8;
aTemp = alphas[ 0 ];
a0 = ( alphas[ 1 ] - aTemp ) / 5;
for( j = 2; j < 6; j++ )
{
aTemp += a0;
alphas[ j ] = aTemp & 0xf000; /// Mask done here to retain as
/// much accuracy as possible
}
alphas[ 6 ] = 0;
alphas[ 7 ] = 0xf000;
}
/// Mask off the original two now
alphas[ 0 ] &= 0xf000;
alphas[ 1 ] &= 0xf000;
/// Now do the 16 pixels in 2 blocks, decompressing 3-bit lookups
aBitSrc1 = ( (UInt32)bytePtr[ 4 ] << 16 ) +
( (UInt32)bytePtr[ 3 ] << 8 ) +
( (UInt32)bytePtr[ 2 ] );
aBitSrc2 = ( (UInt32)bytePtr[ 7 ] << 16 ) +
( (UInt32)bytePtr[ 6 ] << 8 ) +
( (UInt32)bytePtr[ 5 ] );
/// Now decompress color data
srcData += 4; // Alpha was 4 16-bit words worth
//hsAssert( srcData[ 0 ] > srcData[ 1 ], "Invalid block compression for DX5 method" ); /// If not, then it's one-bit
/// alpha, but this is DX5!
colors[ 0 ] = IRGB565To4444( srcData[ 0 ] );
colors[ 1 ] = IRGB565To4444( srcData[ 1 ] );
colors[ 2 ] = IMixTwoThirdsRGB4444( colors[ 0 ], colors[ 1 ] );
colors[ 3 ] = IMixTwoThirdsRGB4444( colors[ 1 ], colors[ 0 ] );
cBitSrc1 = srcData[ 2 ];
cBitSrc2 = srcData[ 3 ];
#ifdef HS_BUILD_FOR_MAC
cBitSrc1 = ISwapWordOrder( cBitSrc1 );
cBitSrc2 = ISwapWordOrder( cBitSrc2 );
#endif
for( j = 0; j < 8; j++ )
{
destBlock[ j ] = alphas[ aBitSrc1 & 0x07 ] | colors[ cBitSrc1 & 0x03 ];
aBitSrc1 >>= 3;
cBitSrc1 >>= 2;
destBlock[ j + 8 ] = alphas[ aBitSrc2 & 0x07 ] | colors[ cBitSrc2 & 0x03 ];
aBitSrc2 >>= 3;
cBitSrc2 >>= 2;
#ifdef HS_BUILD_FOR_MAC
destBlock[ j ] = ISwapWordOrder( destBlock[ j ] );
destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] );
#endif
}
/// Now copy the block to the destination bitmap
/// (Trust me, this is actually *faster* than memcpy for some reason
destData = destBMap->GetAddr16( x, y );
destData[ 0 ] = destBlock[ 0 ];
destData[ 1 ] = destBlock[ 1 ];
destData[ 2 ] = destBlock[ 2 ];
destData[ 3 ] = destBlock[ 3 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 4 ];
destData[ 1 ] = destBlock[ 5 ];
destData[ 2 ] = destBlock[ 6 ];
destData[ 3 ] = destBlock[ 7 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 8 ];
destData[ 1 ] = destBlock[ 9 ];
destData[ 2 ] = destBlock[ 10 ];
destData[ 3 ] = destBlock[ 11 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 12 ];
destData[ 1 ] = destBlock[ 13 ];
destData[ 2 ] = destBlock[ 14 ];
destData[ 3 ] = destBlock[ 15 ];
/// Increment and loop!
srcData += blockSize - 4; /// JUUUST in case our block size is diff
x += 4;
if( x == srcBMap->GetCurrWidth() )
{
x = 0;
y += 4;
}
}
}
//// IUncompressMipmapDXT5To16Weird ///////////////////////////////////////////
//
// UncompressBitmap internal call for DXT5 compression. DXT5 is 3-bit linear
// interpolated alpha channel compression. Output is a 16-bit RGB 4444
// reversed bitmap (Red and blue are swapped ala OpenGL).
// (Note: Annoyingly enough, this is exactly the same as the above function
// EXCEPT for two stupid lines. We can't use function pointers as the two
// function calls are inline. Wouldn't it be nice if we could somehow write
// the inline opcodes beforehand?)
void hsDXTSoftwareCodec::IUncompressMipmapDXT5To16Weird( plMipmap *destBMap, plMipmap *srcBMap )
{
UInt16 *srcData;
UInt16 *destData, destBlock[ 16 ];
UInt32 blockSize;
UInt32 x, y, bMapStride;
UInt16 colors[ 4 ];
Int32 numBlocks, i, j;
UInt8 *bytePtr;
UInt16 alphas[ 8 ], aTemp, a0, a1;
UInt32 aBitSrc1, aBitSrc2;
UInt16 cBitSrc1, cBitSrc2;
/// Setup some nifty stuff
hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" );
hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" );
numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4;
blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words
srcData = (UInt16 *)srcBMap->GetCurrLevelPtr();
// Note our trick here to make sure nothing breaks if GetAddr16's
// formula changes
bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) );
x = y = 0;
/// Loop through the # of blocks (width*height / 16-pixel-blocks)
for( i = 0; i < numBlocks; i++ )
{
/// Per block--determine alpha compression type first
bytePtr = (UInt8 *)srcData;
alphas[ 0 ] = bytePtr[ 0 ];
alphas[ 1 ] = bytePtr[ 1 ];
/// Note that we use the preshifted alphas really as fixed point.
/// The result: more accuracy, and no need to shift the alphas afterwards
if( alphas[ 0 ] > alphas[ 1 ] )
{
/// 8-alpha block: interpolate 6 others
alphas[ 0 ] <<= 8;
alphas[ 1 ] <<= 8;
/// Note that, unlike below, we can't combine a0 and a1 into
/// one value, because that would give us a negative value,
/// and we're using unsigned values here. (i.e. we need all the bits)
aTemp = alphas[ 0 ];
a0 = ( aTemp / 7 );
a1 = ( alphas[ 1 ] / 7 );
for( j = 2; j < 8; j++ )
{
aTemp += a1 - a0;
alphas[ j ] = aTemp & 0xf000; /// Mask done here to retain as
/// much accuracy as possible
}
}
else
{
/// 6-alpha block: interpolate 4 others, then assume last 2 are 0 and 255
alphas[ 0 ] <<= 8;
alphas[ 1 ] <<= 8;
aTemp = alphas[ 0 ];
a0 = ( alphas[ 1 ] - aTemp ) / 5;
for( j = 2; j < 6; j++ )
{
aTemp += a0;
alphas[ j ] = aTemp & 0xf000; /// Mask done here to retain as
/// much accuracy as possible
}
alphas[ 6 ] = 0;
alphas[ 7 ] = 0xf000;
}
/// Mask off the original two now
alphas[ 0 ] &= 0xf000;
alphas[ 1 ] &= 0xf000;
/// Now do the 16 pixels in 2 blocks, decompressing 3-bit lookups
aBitSrc1 = ( (UInt32)bytePtr[ 4 ] << 16 ) +
( (UInt32)bytePtr[ 3 ] << 8 ) +
( (UInt32)bytePtr[ 2 ] );
aBitSrc2 = ( (UInt32)bytePtr[ 7 ] << 16 ) +
( (UInt32)bytePtr[ 6 ] << 8 ) +
( (UInt32)bytePtr[ 5 ] );
/// Now decompress color data
srcData += 4; // Alpha was 4 16-bit words worth
colors[ 0 ] = IRGB565To4444Rev( srcData[ 0 ] );
colors[ 1 ] = IRGB565To4444Rev( srcData[ 1 ] );
colors[ 2 ] = IMixTwoThirdsRGB4444( colors[ 0 ], colors[ 1 ] );
colors[ 3 ] = IMixTwoThirdsRGB4444( colors[ 1 ], colors[ 0 ] );
cBitSrc1 = srcData[ 2 ];
cBitSrc2 = srcData[ 3 ];
#ifdef HS_BUILD_FOR_MAC
cBitSrc1 = ISwapWordOrder( cBitSrc1 );
cBitSrc2 = ISwapWordOrder( cBitSrc2 );
#endif
for( j = 0; j < 8; j++ )
{
destBlock[ j ] = alphas[ aBitSrc1 & 0x07 ] | colors[ cBitSrc1 & 0x03 ];
aBitSrc1 >>= 3;
cBitSrc1 >>= 2;
destBlock[ j + 8 ] = alphas[ aBitSrc2 & 0x07 ] | colors[ cBitSrc2 & 0x03 ];
aBitSrc2 >>= 3;
cBitSrc2 >>= 2;
#ifdef HS_BUILD_FOR_MAC
destBlock[ j ] = ISwapWordOrder( destBlock[ j ] );
destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] );
#endif
}
/// Now copy the block to the destination bitmap
/// (Trust me, this is actually *faster* than memcpy for some reason
destData = destBMap->GetAddr16( x, y );
destData[ 0 ] = destBlock[ 0 ];
destData[ 1 ] = destBlock[ 1 ];
destData[ 2 ] = destBlock[ 2 ];
destData[ 3 ] = destBlock[ 3 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 4 ];
destData[ 1 ] = destBlock[ 5 ];
destData[ 2 ] = destBlock[ 6 ];
destData[ 3 ] = destBlock[ 7 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 8 ];
destData[ 1 ] = destBlock[ 9 ];
destData[ 2 ] = destBlock[ 10 ];
destData[ 3 ] = destBlock[ 11 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 12 ];
destData[ 1 ] = destBlock[ 13 ];
destData[ 2 ] = destBlock[ 14 ];
destData[ 3 ] = destBlock[ 15 ];
/// Increment and loop!
srcData += blockSize - 4; /// JUUUST in case our block size is diff
x += 4;
if( x == srcBMap->GetCurrWidth() )
{
x = 0;
y += 4;
}
}
}
//// IUncompressMipmapDXT5To32 ////////////////////////////////////////////////
//
// UncompressBitmap internal call for DXT5 compression. DXT5 is 3-bit linear
// interpolated alpha channel compression. Output is a 32-bit ARGB 8888 bitmap.
//
// 7.31.2000 - M.Burrack - Created, based on old code (uncredited)
//
// 8.14.2000 - M.Burrack - Optimized on the alpha blending. Now we precalc
// the divided values and run a for loop. This gets
// us only about 10% :(
void hsDXTSoftwareCodec::IUncompressMipmapDXT5To32( plMipmap *destBMap, plMipmap *srcBMap )
{
UInt16 *srcData;
UInt32 *destData, destBlock[ 16 ];
UInt32 blockSize;
UInt32 x, y, bMapStride;
UInt32 colors[ 4 ];
Int32 numBlocks, i, j;
UInt8 *bytePtr;
UInt32 alphas[ 8 ], aTemp, a0, a1;
UInt32 aBitSrc1, aBitSrc2;
UInt16 cBitSrc1, cBitSrc2;
/// Setup some nifty stuff
hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" );
hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" );
numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4;
blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words
srcData = (UInt16 *)srcBMap->GetCurrLevelPtr();
// Note our trick here to make sure nothing breaks if GetAddr32's
// formula changes
bMapStride = (UInt32)( destBMap->GetAddr32( 0, 1 ) - destBMap->GetAddr32( 0, 0 ) );
x = y = 0;
/// Loop through the # of blocks (width*height / 16-pixel-blocks)
for( i = 0; i < numBlocks; i++ )
{
/// Per block--determine alpha compression type first
bytePtr = (UInt8 *)srcData;
alphas[ 0 ] = bytePtr[ 0 ];
alphas[ 1 ] = bytePtr[ 1 ];
/// Note that we use the preshifted alphas really as fixed point.
/// The result: more accuracy, and no need to shift the alphas afterwards
if( alphas[ 0 ] > alphas[ 1 ] )
{
/// 8-alpha block: interpolate 6 others
/* //// Here's the old code, for reference ////
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;
*/
alphas[ 0 ] <<= 24;
alphas[ 1 ] <<= 24;
/// Note that, unlike below, we can't combine a0 and a1 into
/// one value, because that would give us a negative value,
/// and we're using unsigned values here. (i.e. we need all the bits)
aTemp = alphas[ 0 ];
a0 = ( aTemp / 7 ) & 0xff000000;
a1 = ( alphas[ 1 ] / 7 ) & 0xff000000;
for( j = 2; j < 8; j++ )
{
aTemp += a1 - a0;
alphas[ j ] = aTemp;
}
}
else
{
/// 6-alpha block: interpolate 4 others, then assume last 2 are 0 and 255
/* //// Here's the old code, for reference ////
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[ 0 ] <<= 24;
alphas[ 1 ] <<= 24;
aTemp = alphas[ 0 ];
a0 = ( alphas[ 1 ] - aTemp ) / 5;
for( j = 2; j < 6; j++ )
{
aTemp += a0;
alphas[ j ] = aTemp & 0xff000000;
}
alphas[ 6 ] = 0;
alphas[ 7 ] = 255 << 24;
}
/// Now do the 16 pixels in 2 blocks, decompressing 3-bit lookups
aBitSrc1 = ( (UInt32)bytePtr[ 4 ] << 16 ) +
( (UInt32)bytePtr[ 3 ] << 8 ) +
( (UInt32)bytePtr[ 2 ] );
aBitSrc2 = ( (UInt32)bytePtr[ 7 ] << 16 ) +
( (UInt32)bytePtr[ 6 ] << 8 ) +
( (UInt32)bytePtr[ 5 ] );
/// Now decompress color data
srcData += 4; // Alpha was 4 16-bit words worth
//hsAssert( srcData[ 0 ] > srcData[ 1 ], "Invalid block compression for DX5 method" ); /// If not, then it's one-bit
/// alpha, but this is DX5!
colors[ 0 ] = IRGB16To32Bit( srcData[ 0 ] );
colors[ 1 ] = IRGB16To32Bit( srcData[ 1 ] );
colors[ 2 ] = IMixTwoThirdsRGB32( colors[ 0 ], colors[ 1 ] );
colors[ 3 ] = IMixTwoThirdsRGB32( colors[ 1 ], colors[ 0 ] );
cBitSrc1 = srcData[ 2 ];
cBitSrc2 = srcData[ 3 ];
#ifdef HS_BUILD_FOR_MAC
cBitSrc1 = ( cBitSrc1 >> 8 ) | ( ( cBitSrc1 & 0xff ) << 8 );
cBitSrc2 = ( cBitSrc2 >> 8 ) | ( ( cBitSrc2 & 0xff ) << 8 );
#endif
for( j = 0; j < 8; j++ )
{
destBlock[ j ] = alphas[ aBitSrc1 & 0x07 ] | colors[ cBitSrc1 & 0x03 ];
aBitSrc1 >>= 3;
cBitSrc1 >>= 2;
destBlock[ j + 8 ] = alphas[ aBitSrc2 & 0x07 ] | colors[ cBitSrc2 & 0x03 ];
aBitSrc2 >>= 3;
cBitSrc2 >>= 2;
#ifdef HS_BUILD_FOR_MAC
destBlock[ j ] = ISwapDwordOrder( destBlock[ j ] );
destBlock[ j + 8 ] = ISwapDwordOrder( destBlock[ j + 8 ] );
#endif
}
/// Now copy the block to the destination bitmap
/// (Trust me, this is actually *faster* than memcpy for some reason
destData = destBMap->GetAddr32( x, y );
destData[ 0 ] = destBlock[ 0 ];
destData[ 1 ] = destBlock[ 1 ];
destData[ 2 ] = destBlock[ 2 ];
destData[ 3 ] = destBlock[ 3 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 4 ];
destData[ 1 ] = destBlock[ 5 ];
destData[ 2 ] = destBlock[ 6 ];
destData[ 3 ] = destBlock[ 7 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 8 ];
destData[ 1 ] = destBlock[ 9 ];
destData[ 2 ] = destBlock[ 10 ];
destData[ 3 ] = destBlock[ 11 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 12 ];
destData[ 1 ] = destBlock[ 13 ];
destData[ 2 ] = destBlock[ 14 ];
destData[ 3 ] = destBlock[ 15 ];
/// Increment and loop!
srcData += blockSize - 4; /// JUUUST in case our block size is diff
x += 4;
if( x == srcBMap->GetCurrWidth() )
{
x = 0;
y += 4;
}
}
}
//// IUncompressMipmapDXT5ToAInten ////////////////////////////////////////////
//
// UncompressBitmap internal call for DXT5 compression. DXT5 is 3-bit linear
// interpolated alpha channel compression. Output is a 16-bit Alpha-intensity
// map.
void hsDXTSoftwareCodec::IUncompressMipmapDXT5ToAInten( plMipmap *destBMap, plMipmap *srcBMap )
{
UInt16 *srcData;
UInt16 *destData, destBlock[ 16 ];
UInt32 blockSize;
UInt32 x, y, bMapStride;
UInt8 colors[ 4 ];
Int32 numBlocks, i, j;
UInt8 *bytePtr;
UInt16 alphas[ 8 ], aTemp, a0, a1;
UInt32 aBitSrc1, aBitSrc2;
UInt16 cBitSrc1, cBitSrc2;
/// Setup some nifty stuff
hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" );
hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" );
numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4;
blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words
srcData = (UInt16 *)srcBMap->GetCurrLevelPtr();
// Note our trick here to make sure nothing breaks if GetAddr32's
// formula changes
bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) );
x = y = 0;
/// Loop through the # of blocks (width*height / 16-pixel-blocks)
for( i = 0; i < numBlocks; i++ )
{
/// Per block--determine alpha compression type first
bytePtr = (UInt8 *)srcData;
alphas[ 0 ] = bytePtr[ 0 ];
alphas[ 1 ] = bytePtr[ 1 ];
/// Note that we use the preshifted alphas really as fixed point.
/// The result: more accuracy, and no need to shift the alphas afterwards
if( alphas[ 0 ] > alphas[ 1 ] )
{
/// 8-alpha block: interpolate 6 others
alphas[ 0 ] <<= 8;
alphas[ 1 ] <<= 8;
/// Note that, unlike below, we can't combine a0 and a1 into
/// one value, because that would give us a negative value,
/// and we're using unsigned values here. (i.e. we need all the bits)
aTemp = alphas[ 0 ];
a0 = ( aTemp / 7 );
a1 = ( alphas[ 1 ] / 7 );
for( j = 2; j < 8; j++ )
{
aTemp += a1 - a0;
alphas[ j ] = aTemp & 0xff00; /// Mask done here to retain as
/// much accuracy as possible
}
}
else
{
/// 6-alpha block: interpolate 4 others, then assume last 2 are 0 and 255
alphas[ 0 ] <<= 8;
alphas[ 1 ] <<= 8;
aTemp = alphas[ 0 ];
a0 = ( alphas[ 1 ] - aTemp ) / 5;
for( j = 2; j < 6; j++ )
{
aTemp += a0;
alphas[ j ] = aTemp & 0xff00; /// Mask done here to retain as
/// much accuracy as possible
}
alphas[ 6 ] = 0;
alphas[ 7 ] = 0xff00;
}
/// Now do the 16 pixels in 2 blocks, decompressing 3-bit lookups
aBitSrc1 = ( (UInt32)bytePtr[ 4 ] << 16 ) +
( (UInt32)bytePtr[ 3 ] << 8 ) +
( (UInt32)bytePtr[ 2 ] );
aBitSrc2 = ( (UInt32)bytePtr[ 7 ] << 16 ) +
( (UInt32)bytePtr[ 6 ] << 8 ) +
( (UInt32)bytePtr[ 5 ] );
/// Now decompress color data
srcData += 4; // Alpha was 4 16-bit words worth
colors[ 0 ] = (UInt8)IRGB16To32Bit( srcData[ 0 ] );
colors[ 1 ] = (UInt8)IRGB16To32Bit( srcData[ 1 ] );
colors[ 2 ] = IMixTwoThirdsInten( colors[ 0 ], colors[ 1 ] );
colors[ 3 ] = IMixTwoThirdsInten( colors[ 1 ], colors[ 0 ] );
cBitSrc1 = srcData[ 2 ];
cBitSrc2 = srcData[ 3 ];
#ifdef HS_BUILD_FOR_MAC
cBitSrc1 = ( cBitSrc1 >> 8 ) | ( ( cBitSrc1 & 0xff ) << 8 );
cBitSrc2 = ( cBitSrc2 >> 8 ) | ( ( cBitSrc2 & 0xff ) << 8 );
#endif
for( j = 0; j < 8; j++ )
{
destBlock[ j ] = alphas[ aBitSrc1 & 0x07 ] | (UInt16)colors[ cBitSrc1 & 0x03 ];
aBitSrc1 >>= 3;
cBitSrc1 >>= 2;
destBlock[ j + 8 ] = alphas[ aBitSrc2 & 0x07 ] | (UInt16)colors[ cBitSrc2 & 0x03 ];
aBitSrc2 >>= 3;
cBitSrc2 >>= 2;
#ifdef HS_BUILD_FOR_MAC
destBlock[ j ] = ISwapWordOrder( destBlock[ j ] );
destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] );
#endif
}
/// Now copy the block to the destination bitmap
/// (Trust me, this is actually *faster* than memcpy for some reason
destData = destBMap->GetAddr16( x, y );
destData[ 0 ] = destBlock[ 0 ];
destData[ 1 ] = destBlock[ 1 ];
destData[ 2 ] = destBlock[ 2 ];
destData[ 3 ] = destBlock[ 3 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 4 ];
destData[ 1 ] = destBlock[ 5 ];
destData[ 2 ] = destBlock[ 6 ];
destData[ 3 ] = destBlock[ 7 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 8 ];
destData[ 1 ] = destBlock[ 9 ];
destData[ 2 ] = destBlock[ 10 ];
destData[ 3 ] = destBlock[ 11 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 12 ];
destData[ 1 ] = destBlock[ 13 ];
destData[ 2 ] = destBlock[ 14 ];
destData[ 3 ] = destBlock[ 15 ];
/// Increment and loop!
srcData += blockSize - 4; /// JUUUST in case our block size is diff
x += 4;
if( x == srcBMap->GetCurrWidth() )
{
x = 0;
y += 4;
}
}
}
//// IUncompressMipmapDXT1To16 ////////////////////////////////////////////////
//
// UncompressBitmap internal call for DXT1 compression. DXT1 is a simple on/off
// or all-on alpha 'compression'.
//
// Note: this version decompresses to a 1-5-5-5 ARGB format.
void hsDXTSoftwareCodec::IUncompressMipmapDXT1To16( plMipmap *destBMap, plMipmap *srcBMap )
{
UInt16 *srcData, tempW1, tempW2;
UInt16 *destData, destBlock[ 16 ];
UInt32 blockSize;
UInt32 bitSource, bitSource2, x, y, bMapStride;
UInt16 colors[ 4 ];
Int32 numBlocks, i, j;
/// Setup some nifty stuff
hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" );
hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" );
numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4;
blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words
srcData = (UInt16 *)srcBMap->GetCurrLevelPtr();
// Note our trick here to make sure nothing breaks if GetAddr32's
// formula changes
bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) );
x = y = 0;
/// Loop through the # of blocks (width*height / 16-pixel-blocks)
for( i = 0; i < numBlocks; i++ )
{
/// Decompress color data block
colors[ 0 ] = IRGB565To1555( srcData[ 0 ] ) | 0x8000; // Make sure alpha is set
colors[ 1 ] = IRGB565To1555( srcData[ 1 ] ) | 0x8000; // Make sure alpha is set
#ifdef HS_BUILD_FOR_MAC
tempW1 = ISwapWordOrder( srcData[ 0 ] );
tempW2 = ISwapWordOrder( srcData[ 1 ] );
#else
tempW1 = srcData[ 0 ];
tempW2 = srcData[ 1 ];
#endif
if( tempW1 > tempW2 )
{
/// Four-color block--mix the other two
colors[ 2 ] = IMixTwoThirdsRGB1555( colors[ 0 ], colors[ 1 ] ) | 0x8000;//IMixTwoThirdsRGB32( colors[ 0 ], colors[ 1 ] ) | 0xff000000;
colors[ 3 ] = IMixTwoThirdsRGB1555( colors[ 1 ], colors[ 0 ] ) | 0x8000;//IMixTwoThirdsRGB32( colors[ 1 ], colors[ 0 ] ) | 0xff000000;
}
else
{
/// Three-color block and transparent
colors[ 2 ] = IMixEqualRGB1555( colors[ 0 ], colors[ 1 ] ) | 0x8000;//IMixEqualRGB32( colors[ 0 ], colors[ 1 ] ) | 0xff000000;
colors[ 3 ] = 0;
}
bitSource = srcData[ 2 ];
bitSource2 = srcData[ 3 ];
#ifdef HS_BUILD_FOR_MAC
bitSource = ( bitSource >> 8 ) | ( ( bitSource & 0xff ) << 8 );
bitSource2 = ( bitSource2 >> 8 ) | ( ( bitSource2 & 0xff ) << 8 );
#endif
for( j = 0; j < 8; j++ )
{
destBlock[ j ] = colors[ bitSource & 0x03 ];
bitSource >>= 2;
destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ];
bitSource2 >>= 2;
#ifdef HS_BUILD_FOR_MAC
destBlock[ j ] = ISwapWordOrder( destBlock[ j ] );
destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] );
#endif
}
/// Now copy the block to the destination bitmap
/// (Trust me, this is actually *faster* than memcpy for some reason
destData = destBMap->GetAddr16( x, y );
destData[ 0 ] = destBlock[ 0 ];
destData[ 1 ] = destBlock[ 1 ];
destData[ 2 ] = destBlock[ 2 ];
destData[ 3 ] = destBlock[ 3 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 4 ];
destData[ 1 ] = destBlock[ 5 ];
destData[ 2 ] = destBlock[ 6 ];
destData[ 3 ] = destBlock[ 7 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 8 ];
destData[ 1 ] = destBlock[ 9 ];
destData[ 2 ] = destBlock[ 10 ];
destData[ 3 ] = destBlock[ 11 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 12 ];
destData[ 1 ] = destBlock[ 13 ];
destData[ 2 ] = destBlock[ 14 ];
destData[ 3 ] = destBlock[ 15 ];
/// Increment and loop!
srcData += blockSize;
x += 4;
if( x == srcBMap->GetCurrWidth() )
{
x = 0;
y += 4;
}
}
}
//// IUncompressMipmapDXT1To16Weird ///////////////////////////////////////////
//
// UncompressBitmap internal call for DXT1 compression. DXT1 is a simple on/off
// or all-on alpha 'compression'.
//
// Note: this version decompresses to a 5-5-5-1 RGBA format.
void hsDXTSoftwareCodec::IUncompressMipmapDXT1To16Weird( plMipmap *destBMap,
plMipmap *srcBMap )
{
UInt16 *srcData, tempW1, tempW2;
UInt16 *destData, destBlock[ 16 ];
UInt32 blockSize;
UInt32 bitSource, bitSource2, x, y, bMapStride;
UInt16 colors[ 4 ];
Int32 numBlocks, i, j;
/// Setup some nifty stuff
hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" );
hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" );
numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4;
blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words
srcData = (UInt16 *)srcBMap->GetCurrLevelPtr();
// Note our trick here to make sure nothing breaks if GetAddr32's
// formula changes
bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) );
x = y = 0;
/// Loop through the # of blocks (width*height / 16-pixel-blocks)
for( i = 0; i < numBlocks; i++ )
{
/// Decompress color data block
colors[ 0 ] = IRGB565To5551( srcData[ 0 ] ) | 0x0001; // Make sure alpha is set
colors[ 1 ] = IRGB565To5551( srcData[ 1 ] ) | 0x0001; // Make sure alpha is set
#ifdef HS_BUILD_FOR_MAC
tempW1 = ISwapWordOrder( srcData[ 0 ] );
tempW2 = ISwapWordOrder( srcData[ 1 ] );
#else
tempW1 = srcData[ 0 ];
tempW2 = srcData[ 1 ];
#endif
if( tempW1 > tempW2 )
{
/// Four-color block--mix the other two
colors[ 2 ] = IMixTwoThirdsRGB5551( colors[ 0 ], colors[ 1 ] ) | 0x0001;
colors[ 3 ] = IMixTwoThirdsRGB5551( colors[ 1 ], colors[ 0 ] ) | 0x0001;
}
else
{
/// Three-color block and transparent
colors[ 2 ] = IMixEqualRGB5551( colors[ 0 ], colors[ 1 ] ) | 0x0001;
colors[ 3 ] = 0;
}
bitSource = srcData[ 2 ];
bitSource2 = srcData[ 3 ];
#ifdef HS_BUILD_FOR_MAC
bitSource = ( bitSource >> 8 ) | ( ( bitSource & 0xff ) << 8 );
bitSource2 = ( bitSource2 >> 8 ) | ( ( bitSource2 & 0xff ) << 8 );
#endif
for( j = 0; j < 8; j++ )
{
destBlock[ j ] = colors[ bitSource & 0x03 ];
bitSource >>= 2;
destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ];
bitSource2 >>= 2;
#ifdef HS_BUILD_FOR_MAC
destBlock[ j ] = ISwapWordOrder( destBlock[ j ] );
destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] );
#endif
}
/// Now copy the block to the destination bitmap
/// (Trust me, this is actually *faster* than memcpy for some reason
destData = destBMap->GetAddr16( x, y );
destData[ 0 ] = destBlock[ 0 ];
destData[ 1 ] = destBlock[ 1 ];
destData[ 2 ] = destBlock[ 2 ];
destData[ 3 ] = destBlock[ 3 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 4 ];
destData[ 1 ] = destBlock[ 5 ];
destData[ 2 ] = destBlock[ 6 ];
destData[ 3 ] = destBlock[ 7 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 8 ];
destData[ 1 ] = destBlock[ 9 ];
destData[ 2 ] = destBlock[ 10 ];
destData[ 3 ] = destBlock[ 11 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 12 ];
destData[ 1 ] = destBlock[ 13 ];
destData[ 2 ] = destBlock[ 14 ];
destData[ 3 ] = destBlock[ 15 ];
/// Increment and loop!
srcData += blockSize;
x += 4;
if( x == srcBMap->GetCurrWidth() )
{
x = 0;
y += 4;
}
}
}
//// IUncompressMipmapDXT1To32 ////////////////////////////////////////////////
//
// UncompressBitmap internal call for DXT1 compression. DXT1 is a simple on/off
// or all-on alpha 'compression'. Output is a 32-bit ARGB 8888 bitmap.
//
// 7.31.2000 - M.Burrack - Created, based on old code (uncredited)
void hsDXTSoftwareCodec::IUncompressMipmapDXT1To32( plMipmap *destBMap,
plMipmap *srcBMap )
{
UInt16 *srcData, tempW1, tempW2;
UInt32 *destData, destBlock[ 16 ];
UInt32 blockSize;
UInt32 bitSource, bitSource2, x, y, bMapStride;
UInt32 colors[ 4 ];
Int32 numBlocks, i, j;
/// Setup some nifty stuff
hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" );
hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" );
numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4;
blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words
srcData = (UInt16 *)srcBMap->GetCurrLevelPtr();
// Note our trick here to make sure nothing breaks if GetAddr32's
// formula changes
bMapStride = (UInt32)( destBMap->GetAddr32( 0, 1 ) - destBMap->GetAddr32( 0, 0 ) );
x = y = 0;
/// Loop through the # of blocks (width*height / 16-pixel-blocks)
for( i = 0; i < numBlocks; i++ )
{
/// Decompress color data block
colors[ 0 ] = IRGB16To32Bit( srcData[ 0 ] ) | 0xff000000;
colors[ 1 ] = IRGB16To32Bit( srcData[ 1 ] ) | 0xff000000;
#ifdef HS_BUILD_FOR_MAC
tempW1 = ISwapWordOrder( srcData[ 0 ] );
tempW2 = ISwapWordOrder( srcData[ 1 ] );
#else
tempW1 = srcData[ 0 ];
tempW2 = srcData[ 1 ];
#endif
if( tempW1 > tempW2 )
{
/// Four-color block--mix the other two
colors[ 2 ] = IMixTwoThirdsRGB32( colors[ 0 ], colors[ 1 ] ) | 0xff000000;
colors[ 3 ] = IMixTwoThirdsRGB32( colors[ 1 ], colors[ 0 ] ) | 0xff000000;
}
else
{
/// Three-color block and transparent
colors[ 2 ] = IMixEqualRGB32( colors[ 0 ], colors[ 1 ] ) | 0xff000000;
colors[ 3 ] = 0;
}
bitSource = srcData[ 2 ];
bitSource2 = srcData[ 3 ];
#ifdef HS_BUILD_FOR_MAC
bitSource = ( bitSource >> 8 ) | ( ( bitSource & 0xff ) << 8 );
bitSource2 = ( bitSource2 >> 8 ) | ( ( bitSource2 & 0xff ) << 8 );
#endif
for( j = 0; j < 8; j++ )
{
destBlock[ j ] = colors[ bitSource & 0x03 ];
bitSource >>= 2;
destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ];
bitSource2 >>= 2;
#ifdef HS_BUILD_FOR_MAC
destBlock[ j ] = ISwapDwordOrder( destBlock[ j ] );
destBlock[ j + 8 ] = ISwapDwordOrder( destBlock[ j + 8 ] );
#endif
}
/// Now copy the block to the destination bitmap
/// (Trust me, this is actually *faster* than memcpy for some reason
destData = destBMap->GetAddr32( x, y );
destData[ 0 ] = destBlock[ 0 ];
destData[ 1 ] = destBlock[ 1 ];
destData[ 2 ] = destBlock[ 2 ];
destData[ 3 ] = destBlock[ 3 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 4 ];
destData[ 1 ] = destBlock[ 5 ];
destData[ 2 ] = destBlock[ 6 ];
destData[ 3 ] = destBlock[ 7 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 8 ];
destData[ 1 ] = destBlock[ 9 ];
destData[ 2 ] = destBlock[ 10 ];
destData[ 3 ] = destBlock[ 11 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 12 ];
destData[ 1 ] = destBlock[ 13 ];
destData[ 2 ] = destBlock[ 14 ];
destData[ 3 ] = destBlock[ 15 ];
/// Increment and loop!
srcData += blockSize;
x += 4;
if( x == srcBMap->GetCurrWidth() )
{
x = 0;
y += 4;
}
}
}
//// IUncompressMipmapDXT1ToInten /////////////////////////////////////////////
//
// UncompressBitmap internal call for DXT1 compression. DXT1 is a simple on/off
// or all-on alpha 'compression'. Output is an 8-bit intensity bitmap,
// constructed from the blue-color channel of the DXT1 output.
void hsDXTSoftwareCodec::IUncompressMipmapDXT1ToInten( plMipmap *destBMap,
plMipmap *srcBMap )
{
UInt16 *srcData, tempW1, tempW2;
UInt8 *destData, destBlock[ 16 ];
UInt32 blockSize;
UInt32 bitSource, bitSource2, x, y, bMapStride;
UInt8 colors[ 4 ];
Int32 numBlocks, i, j;
/// Setup some nifty stuff
hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" );
hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" );
numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4;
blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words
srcData = (UInt16 *)srcBMap->GetCurrLevelPtr();
// Note our trick here to make sure nothing breaks if GetAddr8's
// formula changes
bMapStride = (UInt32)( destBMap->GetAddr8( 0, 1 ) - destBMap->GetAddr8( 0, 0 ) );
x = y = 0;
/// Loop through the # of blocks (width*height / 16-pixel-blocks)
for( i = 0; i < numBlocks; i++ )
{
/// Decompress color data block (cast will automatically truncate to blue)
colors[ 0 ] = (UInt8)IRGB16To32Bit( srcData[ 0 ] );
colors[ 1 ] = (UInt8)IRGB16To32Bit( srcData[ 1 ] );
#ifdef HS_BUILD_FOR_MAC
tempW1 = ISwapWordOrder( srcData[ 0 ] );
tempW2 = ISwapWordOrder( srcData[ 1 ] );
#else
tempW1 = srcData[ 0 ];
tempW2 = srcData[ 1 ];
#endif
if( tempW1 > tempW2 )
{
/// Four-color block--mix the other two
colors[ 2 ] = IMixTwoThirdsInten( colors[ 0 ], colors[ 1 ] );
colors[ 3 ] = IMixTwoThirdsInten( colors[ 1 ], colors[ 0 ] );
}
else
{
/// Three-color block and transparent
colors[ 2 ] = IMixEqualInten( colors[ 0 ], colors[ 1 ] );
colors[ 3 ] = 0;
}
bitSource = srcData[ 2 ];
bitSource2 = srcData[ 3 ];
#ifdef HS_BUILD_FOR_MAC
bitSource = ( bitSource >> 8 ) | ( ( bitSource & 0xff ) << 8 );
bitSource2 = ( bitSource2 >> 8 ) | ( ( bitSource2 & 0xff ) << 8 );
#endif
for( j = 0; j < 8; j++ )
{
destBlock[ j ] = colors[ bitSource & 0x03 ];
bitSource >>= 2;
destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ];
bitSource2 >>= 2;
}
/// Now copy the block to the destination bitmap
/// (Trust me, this is actually *faster* than memcpy for some reason
destData = destBMap->GetAddr8( x, y );
destData[ 0 ] = destBlock[ 0 ];
destData[ 1 ] = destBlock[ 1 ];
destData[ 2 ] = destBlock[ 2 ];
destData[ 3 ] = destBlock[ 3 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 4 ];
destData[ 1 ] = destBlock[ 5 ];
destData[ 2 ] = destBlock[ 6 ];
destData[ 3 ] = destBlock[ 7 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 8 ];
destData[ 1 ] = destBlock[ 9 ];
destData[ 2 ] = destBlock[ 10 ];
destData[ 3 ] = destBlock[ 11 ];
destData += bMapStride;
destData[ 0 ] = destBlock[ 12 ];
destData[ 1 ] = destBlock[ 13 ];
destData[ 2 ] = destBlock[ 14 ];
destData[ 3 ] = destBlock[ 15 ];
/// Increment and loop!
srcData += blockSize;
x += 4;
if( x == srcBMap->GetCurrWidth() )
{
x = 0;
y += 4;
}
}
}
//// ISwapDwordOrder //////////////////////////////////////////////////////////
UInt32 hsDXTSoftwareCodec::ISwapDwordOrder( UInt32 color )
{
UInt8 a, r, g, b;
a = (UInt8)( color >> 24 );
r = (UInt8)( color >> 16 ) & 0xff;
g = (UInt8)( color >> 8 ) & 0xff;
b = (UInt8)( color & 0xff );
return( ( b << 24 ) | ( g << 16 ) | ( r << 8 ) | a );
}
//// ISwapWordOrder ///////////////////////////////////////////////////////////
UInt16 hsDXTSoftwareCodec::ISwapWordOrder( UInt16 color )
{
return( ( color >> 8 ) | ( color << 8 ) );
}
//// IRGB16To32Bit ////////////////////////////////////////////////////////////
//
// Converts a RGB565 16-bit color into a RGB888 32-bit color. Alpha (upper 8
// bits) is 0. Will be optimized LATER.
//
UInt32 hsDXTSoftwareCodec::IRGB16To32Bit( UInt16 color )
{
UInt32 r, g, b;
#ifdef HS_BUILD_FOR_MAC
color = ( ( color >> 8 ) | ( ( color & 0xff ) << 8 ) );
#endif
b = ( color & 31 ) << 3;
color >>= 5;
g = ( color & 63 ) << ( 2 + 8 );
color >>= 6;
r = ( color & 31 ) << ( 3 + 16 );
return( r + g + b );
}
//// IRGB565To4444 ////////////////////////////////////////////////////////////
//
// Converts a RGB565 16-bit color into a RGB4444 16-bit color. Alpha (upper 8
// bits) is 0.
//
UInt16 hsDXTSoftwareCodec::IRGB565To4444( UInt16 color )
{
UInt16 r, g, b;
#ifdef HS_BUILD_FOR_MAC
color = ISwapWordOrder( color );
#endif
b = ( color & 31 ) >> 1;
color >>= 5;
g = ( color & 63 ) >> 2;
color >>= 6;
g <<= 4;
r = ( color & 31 ) >> 1;
r <<= 8;
return( r + g + b );
}
//// IRGB565To4444Rev /////////////////////////////////////////////////////////
//
// Converts a RGB565 16-bit color into a RGB4444 16-bit color. Alpha (upper 8
// bits) is 0. This is the OpenGL-friendly version (B and R are swapped)
//
UInt16 hsDXTSoftwareCodec::IRGB565To4444Rev( UInt16 color )
{
UInt16 r, g, b;
#ifdef HS_BUILD_FOR_MAC
color = ISwapWordOrder( color );
#endif
r = ( color & 31 ) >> 1;
color >>= 5;
r <<= 8;
g = ( color & 63 ) >> 2;
color >>= 6;
g <<= 4;
b = ( color & 31 ) >> 1;
return( r + g + b );
}
//// IRGB565To1555 ////////////////////////////////////////////////////////////
//
// Converts a RGB565 16-bit color into a RGB1555 16-bit color. Alpha (upper 1
// bit) is 0.
//
UInt16 hsDXTSoftwareCodec::IRGB565To1555( UInt16 color )
{
UInt16 r, g, b;
#ifdef HS_BUILD_FOR_MAC
color = ISwapWordOrder( color );
#endif
b = ( color & 31 );
color >>= 5;
g = ( color & 63 ) >> 1;
g <<= 5;
color >>= 6;
r = ( color & 31 );
r <<= 10;
return( r + g + b );
}
//// IRGB565To5551 ////////////////////////////////////////////////////////////
//
// Converts a RGB565 16-bit color into a RGB5551 16-bit color. Alpha (lowest 1
// bit) is 0.
//
UInt16 hsDXTSoftwareCodec::IRGB565To5551( UInt16 color )
{
UInt16 rg, b;
#ifdef HS_BUILD_FOR_MAC
color = ISwapWordOrder( color );
#endif
rg = color & 0xffc0; /// Masks off red and green
b = ( color & 31 );
b <<= 1;
return( rg | b );
}
//// IMixTwoThirdsRGB32 ///////////////////////////////////////////////////////
//
// Returns a 32-bit RGB888 value resulting from the mixing of 2/3rds of the
// first parameter and 1/3rd of the second. Will be optimized LATER.
//
UInt32 hsDXTSoftwareCodec::IMixTwoThirdsRGB32( UInt32 twoThirds, UInt32 oneThird )
{
UInt32 r, g, b;
r = ( ( twoThirds & 0x00ff0000 ) + ( twoThirds & 0x00ff0000 )
+ ( oneThird & 0x00ff0000 ) ) / 3;
r &= 0x00ff0000;
g = ( ( twoThirds & 0x0000ff00 ) + ( twoThirds & 0x0000ff00 )
+ ( oneThird & 0x0000ff00 ) ) / 3;
g &= 0x0000ff00;
b = ( ( twoThirds & 0x000000ff ) + ( twoThirds & 0x000000ff )
+ ( oneThird & 0x000000ff ) ) / 3;
b &= 0x000000ff;
return( r + g + b );
}
//// IMixTwoThirdsRGB1555 /////////////////////////////////////////////////////
//
// Returns a 16-bit RGB1555 value resulting from the mixing of 2/3rds of the
// first parameter and 1/3rd of the second. Alpha is ignored.
//
UInt16 hsDXTSoftwareCodec::IMixTwoThirdsRGB1555( UInt16 twoThirds, UInt16 oneThird )
{
UInt16 r, g, b;
r = ( ( twoThirds & 0x7c00 ) + ( twoThirds & 0x7c00 )
+ ( oneThird & 0x7c00 ) ) / 3;
r &= 0x7c00;
g = ( ( twoThirds & 0x03e0 ) + ( twoThirds & 0x03e0 )
+ ( oneThird & 0x03e0 ) ) / 3;
g &= 0x03e0;
b = ( ( twoThirds & 0x001f ) + ( twoThirds & 0x001f )
+ ( oneThird & 0x001f ) ) / 3;
b &= 0x001f;
return( r + g + b );
}
//// IMixTwoThirdsRGB5551 /////////////////////////////////////////////////////
//
// Returns a 16-bit RGB5551 value resulting from the mixing of 2/3rds of the
// first parameter and 1/3rd of the second. Alpha is ignored.
//
UInt16 hsDXTSoftwareCodec::IMixTwoThirdsRGB5551( UInt16 twoThirds, UInt16 oneThird )
{
UInt16 r, g, b;
r = ( ( twoThirds & 0xf800 ) + ( twoThirds & 0xf800 )
+ ( oneThird & 0xf800 ) ) / 3;
r &= 0xf800;
g = ( ( twoThirds & 0x07c0 ) + ( twoThirds & 0x07c0 )
+ ( oneThird & 0x07c0 ) ) / 3;
g &= 0x07c0;
b = ( ( twoThirds & 0x003e ) + ( twoThirds & 0x003e )
+ ( oneThird & 0x003e ) ) / 3;
b &= 0x003e;
return( r + g + b );
}
//// IMixTwoThirdsRGB4444 /////////////////////////////////////////////////////
//
// Returns a 16-bit RGB4444 value resulting from the mixing of 2/3rds of the
// first parameter and 1/3rd of the second. Alpha is ignored.
//
UInt16 hsDXTSoftwareCodec::IMixTwoThirdsRGB4444( UInt16 twoThirds, UInt16 oneThird )
{
UInt16 r, g, b;
r = ( ( twoThirds & 0x0f00 ) + ( twoThirds & 0x0f00 )
+ ( oneThird & 0x0f00 ) ) / 3;
r &= 0x0f00;
g = ( ( twoThirds & 0x00f0 ) + ( twoThirds & 0x00f0 )
+ ( oneThird & 0x00f0 ) ) / 3;
g &= 0x00f0;
b = ( ( twoThirds & 0x000f ) + ( twoThirds & 0x000f )
+ ( oneThird & 0x000f ) ) / 3;
b &= 0x000f;
return( r + g + b );
}
//// IMixTwoThirdsInten ///////////////////////////////////////////////////////
//
// Returns an 8-bit intensity value resulting from the mixing of 2/3rds of the
// first parameter and 1/3rd of the second.
//
UInt8 hsDXTSoftwareCodec::IMixTwoThirdsInten( UInt8 twoThirds, UInt8 oneThird )
{
return( ( twoThirds + twoThirds + oneThird ) / 3 );
}
//// IMixEqualRGB32 ///////////////////////////////////////////////////////////
//
// Returns a 32-bit RGB888 value resulting from the mixing of equal parts of
// the two colors given.
//
UInt32 hsDXTSoftwareCodec::IMixEqualRGB32( UInt32 color1, UInt32 color2 )
{
UInt32 r, g, b;
r = ( ( color1 & 0x00ff0000 ) + ( color2 & 0x00ff0000 ) ) >> 1;
r &= 0x00ff0000;
g = ( ( color1 & 0x0000ff00 ) + ( color2 & 0x0000ff00 ) ) >> 1;
g &= 0x0000ff00;
b = ( ( color1 & 0x000000ff ) + ( color2 & 0x000000ff ) ) >> 1;
b &= 0x000000ff;
return( r + g + b );
}
//// IMixEqualRGB1555 /////////////////////////////////////////////////////////
//
// Returns a 16-bit RGB1555 value resulting from the mixing of equal parts of
// the two colors given.
//
UInt16 hsDXTSoftwareCodec::IMixEqualRGB1555( UInt16 color1, UInt16 color2 )
{
UInt16 r, g, b;
r = ( ( color1 & 0x7c00 ) + ( color2 & 0x7c00 ) ) >> 1;
r &= 0x7c00;
g = ( ( color1 & 0x03e0 ) + ( color2 & 0x03e0 ) ) >> 1;
g &= 0x03e0;
b = ( ( color1 & 0x001f ) + ( color2 & 0x001f ) ) >> 1;
b &= 0x001f;
return( r + g + b );
}
//// IMixEqualRGB5551 /////////////////////////////////////////////////////////
//
// Returns a 16-bit RGB5551 value resulting from the mixing of equal parts of
// the two colors given.
//
UInt16 hsDXTSoftwareCodec::IMixEqualRGB5551( UInt16 color1, UInt16 color2 )
{
UInt16 r, g, b;
r = ( ( color1 & 0xf800 ) + ( color2 & 0xf800 ) ) >> 1;
r &= 0xf800;
g = ( ( color1 & 0x07c0 ) + ( color2 & 0x07c0 ) ) >> 1;
g &= 0x07c0;
b = ( ( color1 & 0x003e ) + ( color2 & 0x003e ) ) >> 1;
b &= 0x003e;
return( r + g + b );
}
//// IMixEqualRGB4444 /////////////////////////////////////////////////////////
//
// Returns a 16-bit RGB4444 value resulting from the mixing of equal parts of
// the two colors given.
//
UInt16 hsDXTSoftwareCodec::IMixEqualRGB4444( UInt16 color1, UInt16 color2 )
{
UInt16 r, g, b;
r = ( ( color1 & 0x0f00 ) + ( color2 & 0x0f00 ) ) >> 1;
r &= 0x0f00;
g = ( ( color1 & 0x00f0 ) + ( color2 & 0x00f0 ) ) >> 1;
g &= 0x00f0;
b = ( ( color1 & 0x000f ) + ( color2 & 0x000f ) ) >> 1;
b &= 0x000f;
return( r + g + b );
}
//// IMixEqualInten ///////////////////////////////////////////////////////////
//
// Returns an 8-bit intensity value resulting from the mixing of equal parts of
// the two colors given.
//
UInt8 hsDXTSoftwareCodec::IMixEqualInten( UInt8 color1, UInt8 color2 )
{
return( ( color1 + color2 ) >> 1 );
}
void hsDXTSoftwareCodec::CompressMipmapLevel( plMipmap *uncompressed, plMipmap *compressed )
{
UInt32 *compressedImage = (UInt32 *)compressed->GetCurrLevelPtr();
UInt32 *uncompressedImage = (UInt32 *)uncompressed->GetCurrLevelPtr();
Int32 x, y;
Int32 xMax = uncompressed->GetCurrWidth() >> 2;
Int32 yMax = uncompressed->GetCurrHeight() >> 2;
for (x = 0; x < xMax; ++x)
{
for (y = 0; y < yMax; ++y)
{
UInt8 maxAlpha = 0;
UInt8 minAlpha = 255;
UInt8 oldMaxAlpha = 0;
UInt8 oldMinAlpha = 255;
UInt8 alpha[8];
Int32 maxDistance = 0;
hsRGBAColor32 color[4];
hsBool hasTransparency = false;
Int32 xx, yy;
for (xx = 0; xx < 4; ++xx)
{
for (yy = 0; yy < 4; ++yy)
{
hsRGBAColor32* pixel = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx, 4 * y + yy);
UInt8 pixelAlpha = pixel->a;
if (pixelAlpha != 255)
{
hasTransparency = true;
}
if (compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5)
{
if (pixelAlpha > maxAlpha)
{
maxAlpha = pixelAlpha;
}
if ((pixelAlpha > oldMaxAlpha) && (pixelAlpha < 255))
{
oldMaxAlpha = pixelAlpha;
}
if (pixelAlpha < minAlpha)
{
minAlpha = pixelAlpha;
}
if ((pixelAlpha < oldMinAlpha) && (pixelAlpha > 0))
{
oldMinAlpha = minAlpha;
}
}
Int32 xx2, yy2;
for (xx2 = 0; xx2 < 4; ++xx2)
{
for (yy2 = 0; yy2 < 4; ++yy2)
{
hsRGBAColor32* pixel1 = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx, 4 * y + yy);
hsRGBAColor32* pixel2 = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx2, 4 * y + yy2);
Int32 distance = ColorDistanceARGBSquared(*pixel1, *pixel2);
if (distance >= maxDistance)
{
maxDistance = distance;
color[0] = *pixel1;
color[1] = *pixel2;
}
} // for yy2
} // for xx2
} // for yy
} // for xx
if (oldMinAlpha == 255)
{
hsAssert(oldMaxAlpha == 0, "Weirdness in oldMaxAlpha hsDXTSoftwareCodec::CompressBitmap.");
oldMinAlpha = 0;
oldMaxAlpha = 255;
}
if (compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5)
{
if ((maxAlpha == 255) && (minAlpha == 0))
{
hsAssert(oldMinAlpha <= oldMaxAlpha, "Min > Max in hsDXTSoftwareCodec::CompressBitmap 1.");
alpha[0] = oldMinAlpha;
alpha[1] = oldMaxAlpha;
alpha[2] = (4 * alpha[0] + alpha[1]) / 5; // Bit code 010
alpha[3] = (3 * alpha[0] + 2 * alpha[1]) / 5; // Bit code 011
alpha[4] = (2 * alpha[0] + 3 * alpha[1]) / 5; // Bit code 100
alpha[5] = (alpha[0] + 4 * alpha[1]) / 5; // Bit code 101
alpha[6] = 0; // Bit code 110
alpha[7] = 255; // Bit code 111
}
else if (maxAlpha == minAlpha)
{
alpha[0] = minAlpha;
alpha[1] = maxAlpha;
alpha[2] = (4 * alpha[0] + alpha[1]) / 5; // Bit code 010
alpha[3] = (3 * alpha[0] + 2 * alpha[1]) / 5; // Bit code 011
alpha[4] = (2 * alpha[0] + 3 * alpha[1]) / 5; // Bit code 100
alpha[5] = (alpha[0] + 4 * alpha[1]) / 5; // Bit code 101
alpha[6] = 0; // Bit code 110
alpha[7] = 255; // Bit code 111
}
else
{
hsAssert(minAlpha < maxAlpha, "Min => Max in hsDXTSoftwareCodec::CompressBitmap 3.");
alpha[0] = maxAlpha;
alpha[1] = minAlpha;
alpha[2] = (6 * alpha[0] + alpha[1]) / 7; // bit code 010
alpha[3] = (5 * alpha[0] + 2 * alpha[1]) / 7; // Bit code 011
alpha[4] = (4 * alpha[0] + 3 * alpha[1]) / 7; // Bit code 100
alpha[5] = (3 * alpha[0] + 4 * alpha[1]) / 7; // Bit code 101
alpha[6] = (2 * alpha[0] + 5 * alpha[1]) / 7; // Bit code 110
alpha[7] = (alpha[0] + 6 * alpha[1]) / 7; // Bit code 111
}
}
UInt32 encoding;
UInt16 shortColor[2];
shortColor[0] = Color32To16(color[0]);
shortColor[1] = Color32To16(color[1]);
if ((shortColor[0] == shortColor[1]) ||
((compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1) &&
hasTransparency))
{
encoding = kThreeColorEncoding;
if (shortColor[0] > shortColor[1])
{
UInt16 temp = shortColor[1];
shortColor[1] = shortColor[0];
shortColor[0] = temp;
hsRGBAColor32 temp32 = color[1];
color[1] = color[0];
color[0] = temp32;
}
color[2] = BlendColors32(1, color[0], 1, color[1]);
hsRGBAColor32 black;
black.Set(0, 0, 0, 0);
color[3] = black;
}
else
{
encoding = kFourColorEncoding;
if (shortColor[0] < shortColor[1])
{
UInt16 temp = shortColor[1];
shortColor[1] = shortColor[0];
shortColor[0] = temp;
hsRGBAColor32 temp32 = color[1];
color[1] = color[0];
color[0] = temp32;
}
color[2] = BlendColors32(2, color[0], 1, color[1]);
color[3] = BlendColors32(1, color[0], 2, color[1]);
}
// Process each pixel in block
UInt32 blockSize = compressed->fDirectXInfo.fBlockSize;
UInt32 *block = &compressedImage[(x + xMax * y) * (blockSize >> 2)];
UInt8 *byteBlock = (UInt8 *)block;
UInt8 *alphaBlock = nil;
UInt16 *colorBlock = nil;
if (compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5)
{
alphaBlock = byteBlock;
colorBlock = (UInt16 *)(byteBlock + 8);
alphaBlock[0] = 0;
alphaBlock[1] = 0;
alphaBlock[2] = 0;
alphaBlock[3] = 0;
alphaBlock[4] = 0;
alphaBlock[5] = 0;
alphaBlock[6] = 0;
alphaBlock[7] = 0;
}
else if (compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1)
{
alphaBlock = nil;
colorBlock = (UInt16 *)(byteBlock);
}
else
{
hsAssert(false, "Unrecognized compression scheme.");
}
colorBlock[0] = 0;
colorBlock[1] = 0;
colorBlock[2] = 0;
colorBlock[3] = 0;
for (xx = 0; xx < 4; ++xx)
{
for (yy = 0; yy < 4; ++yy)
{
hsRGBAColor32* pixel = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx, 4 * y + yy);
UInt8 pixelAlpha = pixel->a;
if (alphaBlock)
{
UInt32 alphaIndex = 0;
UInt32 alphaDistance = abs(pixelAlpha - alpha[0]);
Int32 i;
for (i = 1; i < 8; i++)
{
UInt32 distance = abs(pixelAlpha - alpha[i]);
if (distance < alphaDistance)
{
alphaIndex = i;
alphaDistance = distance;
}
}
if (yy < 2)
{
UInt32 alphaShift = 3 * (4 * yy + xx);
UInt32 threeAlphaBytes = alphaIndex << alphaShift;
alphaBlock[2] |= (threeAlphaBytes & 0xff);
alphaBlock[3] |= ((threeAlphaBytes >> 8) & 0xff);
alphaBlock[4] |= ((threeAlphaBytes >> 16) & 0xff);
}
else
{
UInt32 alphaShift = 3 * (4 * (yy - 2) + xx);
UInt32 threeAlphaBytes = alphaIndex << alphaShift;
alphaBlock[5] |= (threeAlphaBytes & 0xff);
alphaBlock[6] |= ((threeAlphaBytes >> 8) & 0xff);
alphaBlock[7] |= ((threeAlphaBytes >> 16) & 0xff);
}
}
UInt32 colorShift = 2 * (4 * yy + xx);
UInt32 colorIndex = 0;
UInt32 colorDistance = ColorDistanceARGBSquared(*pixel, color[0]);
if ((encoding == kThreeColorEncoding) &&
(pixelAlpha == 0))
{
colorIndex = 3;
}
else
{
Int32 i;
Int32 colorMax = (encoding == kThreeColorEncoding) ? 3 : 4;
for (i = 1; i < colorMax; i++)
{
UInt32 distance = ColorDistanceARGBSquared(*pixel, color[i]);
if (distance < colorDistance)
{
colorIndex = i;
colorDistance = distance;
}
}
}
if (yy < 2)
{
UInt32 colorShift = 2 * (4 * yy + xx);
UInt16 colorWord = (UInt16)(colorIndex << colorShift);
colorBlock[2] |= colorWord;
}
else
{
UInt32 colorShift = 2 * (4 * (yy - 2) + xx);
UInt16 colorWord = (UInt16)(colorIndex << colorShift);
colorBlock[3] |= colorWord;
}
} // for yy
} // for xx
if (alphaBlock)
{
alphaBlock[0] = alpha[0];
alphaBlock[1] = alpha[1];
}
colorBlock[0] = shortColor[0];
colorBlock[1] = shortColor[1];
} // for y
} // for x
}
UInt16 hsDXTSoftwareCodec::BlendColors16(UInt16 weight1, UInt16 color1, UInt16 weight2, UInt16 color2)
{
UInt16 r1, r2, g1, g2, b1, b2;
UInt16 r, g, b;
r1 = (color1 >> 8) & 0xf8;
g1 = (color1 >> 3) & 0xfc;
b1 = (color1 << 3) & 0xf8;
r2 = (color2 >> 8) & 0xf8;
g2 = (color2 >> 3) & 0xfc;
b2 = (color2 << 3) & 0xf8;
r = ((UInt16)r1 * weight1 + (UInt16)r2 * weight2)/(weight1 + weight2);
g = ((UInt16)g1 * weight1 + (UInt16)g2 * weight2)/(weight1 + weight2);
b = ((UInt16)b1 * weight1 + (UInt16)b2 * weight2)/(weight1 + weight2);
return ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3);
}
hsRGBAColor32 hsDXTSoftwareCodec::BlendColors32(UInt32 weight1, hsRGBAColor32 color1,
UInt32 weight2, hsRGBAColor32 color2)
{
hsRGBAColor32 result;
result.r = static_cast<UInt8>((color1.r * weight1 + color2.r * weight2)/(weight1 + weight2));
result.g = static_cast<UInt8>((color1.g * weight1 + color2.g * weight2)/(weight1 + weight2));
result.b = static_cast<UInt8>((color1.b * weight1 + color2.b * weight2)/(weight1 + weight2));
return result;
}
Int32 hsDXTSoftwareCodec::ColorDistanceARGBSquared(hsRGBAColor32 color1, hsRGBAColor32 color2)
{
Int32 r1, g1, b1;
Int32 r2, g2, b2;
r1 = color1.r;
r2 = color2.r;
g1 = color1.g;
g2 = color2.g;
b1 = color1.b;
b2 = color2.b;
return (r1 - r2) * (r1 - r2) + (g1 - g2) * (g1 - g2) + (b1 - b2) * (b1 - b2);
}
UInt16 hsDXTSoftwareCodec::Color32To16(hsRGBAColor32 color)
{
UInt8 r = (UInt8)(color.r & 0xf8);
UInt8 g = (UInt8)(color.g & 0xfc);
UInt8 b = (UInt8)(color.b & 0xf8);
return (r << 8) | (g << 3) | (b >> 3);
}
hsBool hsDXTSoftwareCodec::Register()
{
return hsCodecManager::Instance().Register(&(Instance()), plMipmap::kDirectXCompression, 100);
}
//// ICalcCompressedFormat ////////////////////////////////////////////////////
// Determine the DXT compression format based on a bitmap.
UInt8 hsDXTSoftwareCodec::ICalcCompressedFormat( plMipmap *bMap )
{
if( bMap->GetFlags() & plMipmap::kAlphaChannelFlag )
return plMipmap::DirectXInfo::kDXT5;
return plMipmap::DirectXInfo::kDXT1;
}
//// ColorizeCompBitmap ///////////////////////////////////////////////////////
// Colorizes a compressed bitmap according to the color mask given.
hsBool hsDXTSoftwareCodec::ColorizeCompMipmap( plMipmap *bMap, const UInt8 *colorMask )
{
UInt32 numBlocks, blockSize;
UInt16 *srcData, color1, color2, gray, grayDiv2, i;
UInt8 compMasks[ 3 ][ 2 ] = { { 0, 0 }, { 0, 0xff }, { 0xff, 0 } };
/// Sanity checks
hsAssert( bMap != nil, "Nil bitmap passed to ColorizeCompMipmap()" );
hsAssert( colorMask != nil, "Nil color mask passed to ColorizeCompMipmap()" );
hsAssert( bMap->IsCompressed(), "Trying to colorize uncompressed bitmap" );
/// Calc some stuff
numBlocks = ( bMap->GetCurrWidth() * bMap->GetCurrHeight() ) >> 4;
blockSize = bMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words
srcData = (UInt16 *)bMap->GetCurrLevelPtr();
// If we're DXT5, we'll artificially advance srcData so it points to the start
// of the first *color* block, not the first compressed block
if( bMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5 )
srcData += 4; // Alpha was 4 16-bit words worth
/// Loop!
for( ; numBlocks > 0; numBlocks-- )
{
/// Get the two colors to colorize (our decompression scheme will do the rest...
/// handy, eh? :)
#if HS_BUILD_FOR_MAC
color1 = ISwapWordOrder( srcData[ 0 ] );
color2 = ISwapWordOrder( srcData[ 1 ] );
#else
color1 = srcData[ 0 ];
color2 = srcData[ 1 ];
#endif
/// Now colorize using our age-old formula (see hsGMipmap::ColorLevel for details)
gray = ( ( color1 >> 11 ) & 0x1f ) + ( ( color1 >> 6 ) & 0x1f ) + ( color1 & 0x1f );
gray /= 3;
gray = 0x1f - ( ( 0x1f - gray ) >> 1 ); // Lighten it 50%
grayDiv2 = gray >> 1;
color1 = ( ( gray & compMasks[ colorMask[ 0 ] ][ 0 ] ) |
( grayDiv2 & compMasks[ colorMask[ 0 ] ][ 1 ] ) ) << 11;
color1 |= ( ( gray & compMasks[ colorMask[ 1 ] ][ 0 ] ) |
( grayDiv2 & compMasks[ colorMask[ 1 ] ][ 1 ] ) ) << 6;
color1 |= ( ( gray & compMasks[ colorMask[ 2 ] ][ 0 ] ) |
( grayDiv2 & compMasks[ colorMask[ 2 ] ][ 1 ] ) );
gray = ( ( color2 >> 11 ) & 0x1f ) + ( ( color2 >> 6 ) & 0x1f ) + ( color2 & 0x1f );
gray /= 3;
gray = 0x1f - ( ( 0x1f - gray ) >> 1 ); // Lighten it 50%
grayDiv2 = gray >> 1;
color2 = ( ( gray & compMasks[ colorMask[ 0 ] ][ 0 ] ) |
( grayDiv2 & compMasks[ colorMask[ 0 ] ][ 1 ] ) ) << 11;
color2 |= ( ( gray & compMasks[ colorMask[ 1 ] ][ 0 ] ) |
( grayDiv2 & compMasks[ colorMask[ 1 ] ][ 1 ] ) ) << 6;
color2 |= ( ( gray & compMasks[ colorMask[ 2 ] ][ 0 ] ) |
( grayDiv2 & compMasks[ colorMask[ 2 ] ][ 1 ] ) );
/// IMPORTANT: Check to make sure we're preserving the block type
if( srcData[ 0 ] > srcData[ 1 ] && color1 <= color2 )
{
/// NOPE! We better flip the colors and flip all the color refs
/// in this block, to preserve block type
if( color1 == color2 )
color1++; // This is a hack--our rounding may cause this,
// in which case we need to make SURE c1 > c2
else
SWAPVARS( color1, color2, i );
srcData[ 2 ] ^= 0x5555;
srcData[ 3 ] ^= 0x5555;
}
else if( srcData[ 0 ] <= srcData[ 1 ] && color1 > color2 )
{
SWAPVARS( color1, color2, i );
/// 1/2 block w/ alpha -- switch only first two
/// (watch carefully :)
srcData[ 2 ] ^= ( ( ~( srcData[ i ] >> 1 ) ) & 0x5555 );
srcData[ 3 ] ^= ( ( ~( srcData[ i ] >> 1 ) ) & 0x5555 );
/// Spoiler for above: we shift the word right one bit, then
/// not the bits, then mask off the lower bits of the pairs
/// (which of course used to be the upper bits). Now any upper
/// bits that were 0 are now lower bits of 1, and everything
/// else 0's. This we then xor with the original value to
/// flip the lower bits of those pairs whose upper bits are 0.
/// Nifty, eh?
}
/// Write back and go!
#if HS_BUILD_FOR_MAC
srcData[ 0 ] = ISwapWordOrder( color1 );
srcData[ 1 ] = ISwapWordOrder( color2 );
#else
srcData[ 0 ] = color1;
srcData[ 1 ] = color2;
#endif
srcData += blockSize;
}
return true; /// We handled this bitmap
}