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.
2397 lines
87 KiB
2397 lines
87 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/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
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 "HeadSpin.h" |
|
#include "hsColorRGBA.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 |
|
|
|
|
|
bool 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_t format; |
|
plMipmap *compressed = nil; |
|
uint8_t 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 = 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_t flags ) |
|
{ |
|
plMipmap *uncompressed = nil; |
|
int32_t totalSize; |
|
int32_t width, height; |
|
uint8_t 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_t *)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_t *srcBlock, uint8_t flags ) |
|
{ |
|
uint8_t *bytePtr; |
|
uint16_t *wordPtr, tempVal; |
|
uint32_t 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_t *)destBMap->GetCurrLevelPtr(); |
|
for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ ) |
|
{ |
|
wordPtr[ i ] = (uint16_t)( ( ( srcBlock[ i ] >> 16 ) & 0xff00 ) | ( srcBlock[ i ] & 0x00ff ) ); |
|
} |
|
} |
|
else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kInten8 ) |
|
{ |
|
/// 8 bits from blue |
|
bytePtr = (uint8_t *)destBMap->GetCurrLevelPtr(); |
|
for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ ) |
|
{ |
|
bytePtr[ i ] = (uint8_t)( srcBlock[ i ] & 0x00ff ); |
|
} |
|
} |
|
else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB1555 ) |
|
{ |
|
wordPtr = (uint16_t *)destBMap->GetCurrLevelPtr(); |
|
|
|
if( ( flags & hsCodecManager::kCompOrderMask ) == hsCodecManager::kWeirdCompOrder ) |
|
{ |
|
/// Really do 5551 |
|
for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ ) |
|
{ |
|
tempVal = (uint16_t)( ( ( 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_t)( ( ( 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_t *)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_t)( ( ( 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_t)( ( ( 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_t flags ) |
|
{ |
|
uint8_t 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 = 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_t *compressedImage = (uint32_t *)compressed->GetCurrLevelPtr(); |
|
uint32_t *uncompressedImage = (uint32_t *)uncompressed->GetCurrLevelPtr(); |
|
uint32_t blockSize = compressed->fDirectXInfo.fBlockSize; |
|
int32_t x, y; |
|
int32_t xMax = compressed->GetCurrWidth() >> 2; |
|
int32_t yMax = compressed->GetCurrHeight() >> 2; |
|
for (x = 0; x < xMax; ++x) |
|
{ |
|
for (y = 0; y < yMax; ++y) |
|
{ |
|
uint8_t alpha[8]; |
|
uint16_t color[4]; |
|
uint32_t *block = &compressedImage[(x + xMax * y) * (blockSize >> 2)]; |
|
uint8_t *charBlock = (uint8_t *)block; |
|
uint8_t *alphaBlock = nil; |
|
uint8_t *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_t 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_t r, g, b, a; |
|
int32_t xx, yy; |
|
for (xx = 0; xx < 4; ++xx) |
|
{ |
|
for (yy = 0; yy < 4; ++yy) |
|
{ |
|
if (alphaBlock) |
|
{ |
|
uint32_t alphaMask = 0x7; |
|
uint32_t firstThreeBytes = (alphaBlock[4] << 16) + (alphaBlock[3] << 8) + alphaBlock[2]; |
|
uint32_t secondThreeBytes = (alphaBlock[7] << 16) + (alphaBlock[6] << 8) + alphaBlock[5]; |
|
uint32_t alphaIndex; |
|
uint32_t 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_t colorMask = 0x3; |
|
uint32_t colorDWord = (colorBlock[7] << 24) | (colorBlock[6] << 16) | |
|
(colorBlock[5] << 8) | colorBlock[4]; |
|
uint32_t colorShift = 2 * (4 * yy + xx); |
|
uint32_t colorIndex = (colorDWord >> colorShift) & colorMask; |
|
|
|
if ((encoding == kThreeColorEncoding) && (colorIndex == 3)) |
|
{ |
|
r = g = b = a = 0; |
|
} |
|
else |
|
{ |
|
r = (uint8_t)((color[colorIndex] >> 8) & 0xf8); |
|
g = (uint8_t)((color[colorIndex] >> 3) & 0xfc); |
|
b = (uint8_t)((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_t 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_t *srcData; |
|
uint16_t *destData, destBlock[ 16 ]; |
|
uint32_t blockSize; |
|
uint32_t x, y, bMapStride; |
|
uint16_t colors[ 4 ]; |
|
int32_t numBlocks, i, j; |
|
uint8_t *bytePtr; |
|
uint16_t alphas[ 8 ], aTemp, a0, a1; |
|
|
|
uint32_t aBitSrc1, aBitSrc2; |
|
uint16_t 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_t *)srcBMap->GetCurrLevelPtr(); |
|
// Note our trick here to make sure nothing breaks if GetAddr16's |
|
// formula changes |
|
bMapStride = (uint32_t)( 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_t *)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_t)bytePtr[ 4 ] << 16 ) + |
|
( (uint32_t)bytePtr[ 3 ] << 8 ) + |
|
( (uint32_t)bytePtr[ 2 ] ); |
|
aBitSrc2 = ( (uint32_t)bytePtr[ 7 ] << 16 ) + |
|
( (uint32_t)bytePtr[ 6 ] << 8 ) + |
|
( (uint32_t)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 = hsToLE16( srcData[ 2 ] ); |
|
cBitSrc2 = hsToLE16( srcData[ 3 ] ); |
|
|
|
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; |
|
|
|
destBlock[ j ] = hsToLE16( destBlock[ j ] ); |
|
destBlock[ j + 8 ] = hsToLE16( destBlock[ j + 8 ] ); |
|
} |
|
|
|
/// 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_t *srcData; |
|
uint16_t *destData, destBlock[ 16 ]; |
|
uint32_t blockSize; |
|
uint32_t x, y, bMapStride; |
|
uint16_t colors[ 4 ]; |
|
int32_t numBlocks, i, j; |
|
uint8_t *bytePtr; |
|
uint16_t alphas[ 8 ], aTemp, a0, a1; |
|
|
|
uint32_t aBitSrc1, aBitSrc2; |
|
uint16_t 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_t *)srcBMap->GetCurrLevelPtr(); |
|
// Note our trick here to make sure nothing breaks if GetAddr16's |
|
// formula changes |
|
bMapStride = (uint32_t)( 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_t *)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_t)bytePtr[ 4 ] << 16 ) + |
|
( (uint32_t)bytePtr[ 3 ] << 8 ) + |
|
( (uint32_t)bytePtr[ 2 ] ); |
|
aBitSrc2 = ( (uint32_t)bytePtr[ 7 ] << 16 ) + |
|
( (uint32_t)bytePtr[ 6 ] << 8 ) + |
|
( (uint32_t)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 = hsToLE16( srcData[ 2 ] ); |
|
cBitSrc2 = hsToLE16( srcData[ 3 ] ); |
|
|
|
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; |
|
|
|
destBlock[ j ] = hsToLE16( destBlock[ j ] ); |
|
destBlock[ j + 8 ] = hsToLE16( destBlock[ j + 8 ] ); |
|
} |
|
|
|
/// 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_t *srcData; |
|
uint32_t *destData, destBlock[ 16 ]; |
|
uint32_t blockSize; |
|
uint32_t x, y, bMapStride; |
|
uint32_t colors[ 4 ]; |
|
int32_t numBlocks, i, j; |
|
uint8_t *bytePtr; |
|
uint32_t alphas[ 8 ], aTemp, a0, a1; |
|
|
|
uint32_t aBitSrc1, aBitSrc2; |
|
uint16_t 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_t *)srcBMap->GetCurrLevelPtr(); |
|
// Note our trick here to make sure nothing breaks if GetAddr32's |
|
// formula changes |
|
bMapStride = (uint32_t)( 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_t *)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_t)bytePtr[ 4 ] << 16 ) + |
|
( (uint32_t)bytePtr[ 3 ] << 8 ) + |
|
( (uint32_t)bytePtr[ 2 ] ); |
|
aBitSrc2 = ( (uint32_t)bytePtr[ 7 ] << 16 ) + |
|
( (uint32_t)bytePtr[ 6 ] << 8 ) + |
|
( (uint32_t)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 = hsToLE16( srcData[ 2 ] ); |
|
cBitSrc2 = hsToLE16( srcData[ 3 ] ); |
|
|
|
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; |
|
|
|
destBlock[ j ] = hsToLE32( destBlock[ j ] ); |
|
destBlock[ j + 8 ] = hsToLE32( destBlock[ j + 8 ] ); |
|
} |
|
|
|
/// 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_t *srcData; |
|
uint16_t *destData, destBlock[ 16 ]; |
|
uint32_t blockSize; |
|
uint32_t x, y, bMapStride; |
|
uint8_t colors[ 4 ]; |
|
int32_t numBlocks, i, j; |
|
uint8_t *bytePtr; |
|
uint16_t alphas[ 8 ], aTemp, a0, a1; |
|
|
|
uint32_t aBitSrc1, aBitSrc2; |
|
uint16_t 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_t *)srcBMap->GetCurrLevelPtr(); |
|
// Note our trick here to make sure nothing breaks if GetAddr32's |
|
// formula changes |
|
bMapStride = (uint32_t)( 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_t *)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_t)bytePtr[ 4 ] << 16 ) + |
|
( (uint32_t)bytePtr[ 3 ] << 8 ) + |
|
( (uint32_t)bytePtr[ 2 ] ); |
|
aBitSrc2 = ( (uint32_t)bytePtr[ 7 ] << 16 ) + |
|
( (uint32_t)bytePtr[ 6 ] << 8 ) + |
|
( (uint32_t)bytePtr[ 5 ] ); |
|
|
|
/// Now decompress color data |
|
srcData += 4; // Alpha was 4 16-bit words worth |
|
colors[ 0 ] = (uint8_t)IRGB16To32Bit( srcData[ 0 ] ); |
|
colors[ 1 ] = (uint8_t)IRGB16To32Bit( srcData[ 1 ] ); |
|
colors[ 2 ] = IMixTwoThirdsInten( colors[ 0 ], colors[ 1 ] ); |
|
colors[ 3 ] = IMixTwoThirdsInten( colors[ 1 ], colors[ 0 ] ); |
|
|
|
cBitSrc1 = hsToLE16( srcData[ 2 ] ); |
|
cBitSrc2 = hsToLE16( srcData[ 3 ] ); |
|
|
|
for( j = 0; j < 8; j++ ) |
|
{ |
|
destBlock[ j ] = alphas[ aBitSrc1 & 0x07 ] | (uint16_t)colors[ cBitSrc1 & 0x03 ]; |
|
aBitSrc1 >>= 3; |
|
cBitSrc1 >>= 2; |
|
destBlock[ j + 8 ] = alphas[ aBitSrc2 & 0x07 ] | (uint16_t)colors[ cBitSrc2 & 0x03 ]; |
|
aBitSrc2 >>= 3; |
|
cBitSrc2 >>= 2; |
|
|
|
destBlock[ j ] = hsToLE16( destBlock[ j ] ); |
|
destBlock[ j + 8 ] = hsToLE16( destBlock[ j + 8 ] ); |
|
} |
|
|
|
/// 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_t *srcData, tempW1, tempW2; |
|
uint16_t *destData, destBlock[ 16 ]; |
|
uint32_t blockSize; |
|
uint32_t bitSource, bitSource2, x, y, bMapStride; |
|
uint16_t colors[ 4 ]; |
|
int32_t 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_t *)srcBMap->GetCurrLevelPtr(); |
|
// Note our trick here to make sure nothing breaks if GetAddr32's |
|
// formula changes |
|
bMapStride = (uint32_t)( 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 |
|
|
|
tempW1 = hsToLE16( srcData[ 0 ] ); |
|
tempW2 = hsToLE16( srcData[ 1 ] ); |
|
|
|
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 = hsToLE16( srcData[ 2 ] ); |
|
bitSource2 = hsToLE16( srcData[ 3 ] ); |
|
|
|
for( j = 0; j < 8; j++ ) |
|
{ |
|
destBlock[ j ] = colors[ bitSource & 0x03 ]; |
|
bitSource >>= 2; |
|
destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ]; |
|
bitSource2 >>= 2; |
|
|
|
destBlock[ j ] = hsToLE16( destBlock[ j ] ); |
|
destBlock[ j + 8 ] = hsToLE16( destBlock[ j + 8 ] ); |
|
} |
|
|
|
/// 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_t *srcData, tempW1, tempW2; |
|
uint16_t *destData, destBlock[ 16 ]; |
|
uint32_t blockSize; |
|
uint32_t bitSource, bitSource2, x, y, bMapStride; |
|
uint16_t colors[ 4 ]; |
|
int32_t 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_t *)srcBMap->GetCurrLevelPtr(); |
|
// Note our trick here to make sure nothing breaks if GetAddr32's |
|
// formula changes |
|
bMapStride = (uint32_t)( 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 |
|
|
|
tempW1 = hsToLE16( srcData[ 0 ] ); |
|
tempW2 = hsToLE16( srcData[ 1 ] ); |
|
|
|
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 = hsToLE16( srcData[ 2 ] ); |
|
bitSource2 = hsToLE16( srcData[ 3 ] ); |
|
|
|
for( j = 0; j < 8; j++ ) |
|
{ |
|
destBlock[ j ] = colors[ bitSource & 0x03 ]; |
|
bitSource >>= 2; |
|
destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ]; |
|
bitSource2 >>= 2; |
|
|
|
destBlock[ j ] = hsToLE16( destBlock[ j ] ); |
|
destBlock[ j + 8 ] = hsToLE16( destBlock[ j + 8 ] ); |
|
} |
|
|
|
/// 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_t *srcData, tempW1, tempW2; |
|
uint32_t *destData, destBlock[ 16 ]; |
|
uint32_t blockSize; |
|
uint32_t bitSource, bitSource2, x, y, bMapStride; |
|
uint32_t colors[ 4 ]; |
|
int32_t 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_t *)srcBMap->GetCurrLevelPtr(); |
|
// Note our trick here to make sure nothing breaks if GetAddr32's |
|
// formula changes |
|
bMapStride = (uint32_t)( 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; |
|
|
|
tempW1 = hsToLE16( srcData[ 0 ] ); |
|
tempW2 = hsToLE16( srcData[ 1 ] ); |
|
|
|
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 = hsToLE16( srcData[ 2 ] ); |
|
bitSource2 = hsToLE16( srcData[ 3 ] ); |
|
|
|
for( j = 0; j < 8; j++ ) |
|
{ |
|
destBlock[ j ] = colors[ bitSource & 0x03 ]; |
|
bitSource >>= 2; |
|
destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ]; |
|
bitSource2 >>= 2; |
|
|
|
destBlock[ j ] = hsToLE32( destBlock[ j ] ); |
|
destBlock[ j + 8 ] = hsToLE32( destBlock[ j + 8 ] ); |
|
} |
|
|
|
/// 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_t *srcData, tempW1, tempW2; |
|
uint8_t *destData, destBlock[ 16 ]; |
|
uint32_t blockSize; |
|
uint32_t bitSource, bitSource2, x, y, bMapStride; |
|
uint8_t colors[ 4 ]; |
|
int32_t 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_t *)srcBMap->GetCurrLevelPtr(); |
|
// Note our trick here to make sure nothing breaks if GetAddr8's |
|
// formula changes |
|
bMapStride = (uint32_t)( 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_t)IRGB16To32Bit( srcData[ 0 ] ); |
|
colors[ 1 ] = (uint8_t)IRGB16To32Bit( srcData[ 1 ] ); |
|
|
|
tempW1 = hsToLE16( srcData[ 0 ] ); |
|
tempW2 = hsToLE16( srcData[ 1 ] ); |
|
|
|
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 = hsToLE16( srcData[ 2 ] ); |
|
bitSource2 = hsToLE16( srcData[ 3 ] ); |
|
|
|
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; |
|
} |
|
} |
|
} |
|
|
|
//// IRGB16To32Bit //////////////////////////////////////////////////////////// |
|
// |
|
// Converts a RGB565 16-bit color into a RGB888 32-bit color. Alpha (upper 8 |
|
// bits) is 0. Will be optimized LATER. |
|
// |
|
|
|
uint32_t hsDXTSoftwareCodec::IRGB16To32Bit( uint16_t color ) |
|
{ |
|
uint32_t r, g, b; |
|
|
|
color = hsToLE16(color); |
|
|
|
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_t hsDXTSoftwareCodec::IRGB565To4444( uint16_t color ) |
|
{ |
|
uint16_t r, g, b; |
|
|
|
color = hsToLE16( color ); |
|
|
|
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_t hsDXTSoftwareCodec::IRGB565To4444Rev( uint16_t color ) |
|
{ |
|
uint16_t r, g, b; |
|
|
|
color = hsToLE16( color ); |
|
|
|
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_t hsDXTSoftwareCodec::IRGB565To1555( uint16_t color ) |
|
{ |
|
uint16_t r, g, b; |
|
|
|
color = hsToLE16( color ); |
|
|
|
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_t hsDXTSoftwareCodec::IRGB565To5551( uint16_t color ) |
|
{ |
|
uint16_t rg, b; |
|
|
|
color = hsToLE16( color ); |
|
|
|
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_t hsDXTSoftwareCodec::IMixTwoThirdsRGB32( uint32_t twoThirds, uint32_t oneThird ) |
|
{ |
|
uint32_t 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_t hsDXTSoftwareCodec::IMixTwoThirdsRGB1555( uint16_t twoThirds, uint16_t oneThird ) |
|
{ |
|
uint16_t 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_t hsDXTSoftwareCodec::IMixTwoThirdsRGB5551( uint16_t twoThirds, uint16_t oneThird ) |
|
{ |
|
uint16_t 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_t hsDXTSoftwareCodec::IMixTwoThirdsRGB4444( uint16_t twoThirds, uint16_t oneThird ) |
|
{ |
|
uint16_t 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_t hsDXTSoftwareCodec::IMixTwoThirdsInten( uint8_t twoThirds, uint8_t 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_t hsDXTSoftwareCodec::IMixEqualRGB32( uint32_t color1, uint32_t color2 ) |
|
{ |
|
uint32_t 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_t hsDXTSoftwareCodec::IMixEqualRGB1555( uint16_t color1, uint16_t color2 ) |
|
{ |
|
uint16_t 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_t hsDXTSoftwareCodec::IMixEqualRGB5551( uint16_t color1, uint16_t color2 ) |
|
{ |
|
uint16_t 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_t hsDXTSoftwareCodec::IMixEqualRGB4444( uint16_t color1, uint16_t color2 ) |
|
{ |
|
uint16_t 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_t hsDXTSoftwareCodec::IMixEqualInten( uint8_t color1, uint8_t color2 ) |
|
{ |
|
return( ( color1 + color2 ) >> 1 ); |
|
} |
|
|
|
|
|
|
|
void hsDXTSoftwareCodec::CompressMipmapLevel( plMipmap *uncompressed, plMipmap *compressed ) |
|
{ |
|
uint32_t *compressedImage = (uint32_t *)compressed->GetCurrLevelPtr(); |
|
uint32_t *uncompressedImage = (uint32_t *)uncompressed->GetCurrLevelPtr(); |
|
int32_t x, y; |
|
int32_t xMax = uncompressed->GetCurrWidth() >> 2; |
|
int32_t yMax = uncompressed->GetCurrHeight() >> 2; |
|
for (x = 0; x < xMax; ++x) |
|
{ |
|
for (y = 0; y < yMax; ++y) |
|
{ |
|
uint8_t maxAlpha = 0; |
|
uint8_t minAlpha = 255; |
|
uint8_t oldMaxAlpha = 0; |
|
uint8_t oldMinAlpha = 255; |
|
uint8_t alpha[8]; |
|
int32_t maxDistance = 0; |
|
hsRGBAColor32 color[4]; |
|
bool hasTransparency = false; |
|
|
|
int32_t 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_t 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_t 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_t 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_t encoding; |
|
uint16_t 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_t 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_t 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_t blockSize = compressed->fDirectXInfo.fBlockSize; |
|
uint32_t *block = &compressedImage[(x + xMax * y) * (blockSize >> 2)]; |
|
uint8_t *byteBlock = (uint8_t *)block; |
|
uint8_t *alphaBlock = nil; |
|
uint16_t *colorBlock = nil; |
|
if (compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5) |
|
{ |
|
alphaBlock = byteBlock; |
|
colorBlock = (uint16_t *)(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_t *)(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_t pixelAlpha = pixel->a; |
|
if (alphaBlock) |
|
{ |
|
uint32_t alphaIndex = 0; |
|
uint32_t alphaDistance = abs(pixelAlpha - alpha[0]); |
|
|
|
int32_t i; |
|
for (i = 1; i < 8; i++) |
|
{ |
|
uint32_t distance = abs(pixelAlpha - alpha[i]); |
|
if (distance < alphaDistance) |
|
{ |
|
alphaIndex = i; |
|
alphaDistance = distance; |
|
} |
|
} |
|
|
|
if (yy < 2) |
|
{ |
|
uint32_t alphaShift = 3 * (4 * yy + xx); |
|
uint32_t threeAlphaBytes = alphaIndex << alphaShift; |
|
alphaBlock[2] |= (threeAlphaBytes & 0xff); |
|
alphaBlock[3] |= ((threeAlphaBytes >> 8) & 0xff); |
|
alphaBlock[4] |= ((threeAlphaBytes >> 16) & 0xff); |
|
} |
|
else |
|
{ |
|
uint32_t alphaShift = 3 * (4 * (yy - 2) + xx); |
|
uint32_t threeAlphaBytes = alphaIndex << alphaShift; |
|
alphaBlock[5] |= (threeAlphaBytes & 0xff); |
|
alphaBlock[6] |= ((threeAlphaBytes >> 8) & 0xff); |
|
alphaBlock[7] |= ((threeAlphaBytes >> 16) & 0xff); |
|
} |
|
} |
|
|
|
uint32_t colorShift = 2 * (4 * yy + xx); |
|
uint32_t colorIndex = 0; |
|
uint32_t colorDistance = ColorDistanceARGBSquared(*pixel, color[0]); |
|
|
|
if ((encoding == kThreeColorEncoding) && |
|
(pixelAlpha == 0)) |
|
{ |
|
colorIndex = 3; |
|
} |
|
else |
|
{ |
|
int32_t i; |
|
int32_t colorMax = (encoding == kThreeColorEncoding) ? 3 : 4; |
|
for (i = 1; i < colorMax; i++) |
|
{ |
|
uint32_t distance = ColorDistanceARGBSquared(*pixel, color[i]); |
|
if (distance < colorDistance) |
|
{ |
|
colorIndex = i; |
|
colorDistance = distance; |
|
} |
|
} |
|
} |
|
|
|
if (yy < 2) |
|
{ |
|
uint32_t colorShift = 2 * (4 * yy + xx); |
|
uint16_t colorWord = (uint16_t)(colorIndex << colorShift); |
|
colorBlock[2] |= colorWord; |
|
} |
|
else |
|
{ |
|
uint32_t colorShift = 2 * (4 * (yy - 2) + xx); |
|
uint16_t colorWord = (uint16_t)(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_t hsDXTSoftwareCodec::BlendColors16(uint16_t weight1, uint16_t color1, uint16_t weight2, uint16_t color2) |
|
{ |
|
uint16_t r1, r2, g1, g2, b1, b2; |
|
uint16_t 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_t)r1 * weight1 + (uint16_t)r2 * weight2)/(weight1 + weight2); |
|
g = ((uint16_t)g1 * weight1 + (uint16_t)g2 * weight2)/(weight1 + weight2); |
|
b = ((uint16_t)b1 * weight1 + (uint16_t)b2 * weight2)/(weight1 + weight2); |
|
|
|
return ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3); |
|
} |
|
|
|
|
|
hsRGBAColor32 hsDXTSoftwareCodec::BlendColors32(uint32_t weight1, hsRGBAColor32 color1, |
|
uint32_t weight2, hsRGBAColor32 color2) |
|
{ |
|
hsRGBAColor32 result; |
|
|
|
result.r = static_cast<uint8_t>((color1.r * weight1 + color2.r * weight2)/(weight1 + weight2)); |
|
result.g = static_cast<uint8_t>((color1.g * weight1 + color2.g * weight2)/(weight1 + weight2)); |
|
result.b = static_cast<uint8_t>((color1.b * weight1 + color2.b * weight2)/(weight1 + weight2)); |
|
|
|
return result; |
|
} |
|
|
|
|
|
int32_t hsDXTSoftwareCodec::ColorDistanceARGBSquared(hsRGBAColor32 color1, hsRGBAColor32 color2) |
|
{ |
|
int32_t r1, g1, b1; |
|
int32_t 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_t hsDXTSoftwareCodec::Color32To16(hsRGBAColor32 color) |
|
{ |
|
uint8_t r = (uint8_t)(color.r & 0xf8); |
|
uint8_t g = (uint8_t)(color.g & 0xfc); |
|
uint8_t b = (uint8_t)(color.b & 0xf8); |
|
|
|
return (r << 8) | (g << 3) | (b >> 3); |
|
} |
|
|
|
bool hsDXTSoftwareCodec::Register() |
|
{ |
|
return hsCodecManager::Instance().Register(&(Instance()), plMipmap::kDirectXCompression, 100); |
|
} |
|
|
|
//// ICalcCompressedFormat //////////////////////////////////////////////////// |
|
// Determine the DXT compression format based on a bitmap. |
|
|
|
uint8_t 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. |
|
|
|
bool hsDXTSoftwareCodec::ColorizeCompMipmap( plMipmap *bMap, const uint8_t *colorMask ) |
|
{ |
|
uint32_t numBlocks, blockSize; |
|
uint16_t *srcData, color1, color2, gray, grayDiv2, i; |
|
uint8_t 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_t *)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? :) |
|
color1 = hsToLE16( srcData[ 0 ] ); |
|
color2 = hsToLE16( srcData[ 1 ] ); |
|
|
|
/// 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 uint16_t 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! |
|
srcData[ 0 ] = hsToLE16( color1 ); |
|
srcData[ 1 ] = hsToLE16( color2 ); |
|
|
|
srcData += blockSize; |
|
} |
|
|
|
return true; /// We handled this bitmap |
|
} |
|
|
|
|