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.
395 lines
10 KiB
395 lines
10 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
|
|
#ifdef _WIN32 |
|
#include <windows.h> |
|
#endif |
|
#include "bitmap/imageformat.h" |
|
#include "basetypes.h" |
|
#include "tier0/dbg.h" |
|
#include <malloc.h> |
|
#include <memory.h> |
|
#include "nvtc.h" |
|
#include "mathlib/mathlib.h" |
|
#include "mathlib/vector.h" |
|
#include "utlmemory.h" |
|
#include "tier1/strtools.h" |
|
#include "s3tc_decode.h" |
|
#include "utlvector.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
// This is in s3tc.lib. Nvidia added it specially for us. It can be set to 4, 8, or 12. |
|
// When set to 8 or 12, it generates a palette given an 8x4 or 12x4 texture. |
|
extern int S3TC_BLOCK_WIDTH; |
|
|
|
|
|
class S3Palette |
|
{ |
|
public: |
|
S3RGBA m_Colors[4]; |
|
}; |
|
|
|
|
|
class S3TCBlock_DXT1 |
|
{ |
|
public: |
|
unsigned short m_Ref1; // The two colors that this block blends betwixt. |
|
unsigned short m_Ref2; |
|
unsigned int m_PixelBits; |
|
}; |
|
|
|
|
|
class S3TCBlock_DXT5 |
|
{ |
|
public: |
|
unsigned char m_AlphaRef[2]; |
|
unsigned char m_AlphaBits[6]; |
|
|
|
unsigned short m_Ref1; // The two colors that this block blends betwixt. |
|
unsigned short m_Ref2; |
|
unsigned int m_PixelBits; |
|
}; |
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------ // |
|
// S3TCBlock |
|
// ------------------------------------------------------------------------------------------ // |
|
|
|
int ReadBitInt( const char *pBits, int iBaseBit, int nBits ) |
|
{ |
|
int ret = 0; |
|
for ( int i=0; i < nBits; i++ ) |
|
{ |
|
int iBit = iBaseBit + i; |
|
int val = ((pBits[iBit>>3] >> (iBit&7)) & 1) << i; |
|
ret |= val; |
|
} |
|
return ret; |
|
} |
|
|
|
void WriteBitInt( char *pBits, int iBaseBit, int nBits, int val ) |
|
{ |
|
for ( int i=0; i < nBits; i++ ) |
|
{ |
|
int iBit = iBaseBit + i; |
|
pBits[iBit>>3] &= ~(1 << (iBit & 7)); |
|
if ( (val >> i) & 1 ) |
|
pBits[iBit>>3] |= (1 << (iBit & 7)); |
|
} |
|
} |
|
|
|
int S3TC_BytesPerBlock( ImageFormat format ) |
|
{ |
|
if ( format == IMAGE_FORMAT_DXT1 || format == IMAGE_FORMAT_ATI1N ) |
|
{ |
|
return 8; |
|
} |
|
else |
|
{ |
|
Assert( format == IMAGE_FORMAT_DXT5 || format == IMAGE_FORMAT_ATI2N ); |
|
return 16; |
|
} |
|
} |
|
|
|
|
|
/* |
|
|
|
// We're not using this, but I'll keep it around for reference. |
|
void S3TC_BuildPalette( ImageFormat format, const char *pS3Block, S3RGBA palette[4] ) |
|
{ |
|
if ( format == IMAGE_FORMAT_DXT1 ) |
|
{ |
|
const S3TCBlock_DXT1 *pBlock = reinterpret_cast<const S3TCBlock_DXT1 *>( pS3Block ); |
|
|
|
palette[0] = S3TC_RGBAFrom565( pBlock->m_Ref1, 255 ); |
|
|
|
if ( pBlock->m_Ref1 <= pBlock->m_Ref2 ) |
|
{ |
|
// Opaque and transparent texels are defined. The lookup is 3 colors. 11 means |
|
// a black, transparent pixel. |
|
palette[1] = S3TC_RGBAFrom565( pBlock->m_Ref2, 255 ); |
|
palette[2] = S3TC_RGBABlend( palette[0], palette[1], 1, 1, 2 ); |
|
palette[3].r = palette[3].g = palette[3].b = palette[3].a = 0; |
|
} |
|
else |
|
{ |
|
// Only opaque texels are defined. The lookup is 4 colors. |
|
palette[1] = S3TC_RGBAFrom565( pBlock->m_Ref2, 255 ); |
|
palette[2] = S3TC_RGBABlend( palette[0], palette[1], 2, 1, 3 ); |
|
palette[3] = S3TC_RGBABlend( palette[0], palette[1], 1, 2, 3 ); |
|
} |
|
} |
|
else |
|
{ |
|
Assert( format == IMAGE_FORMAT_DXT5 ); |
|
} |
|
} |
|
|
|
*/ |
|
|
|
|
|
S3PaletteIndex S3TC_GetPixelPaletteIndex( ImageFormat format, const char *pS3Block, int x, int y ) |
|
{ |
|
Assert( x >= 0 && x < 4 ); |
|
Assert( y >= 0 && y < 4 ); |
|
int iQuadPixel = y*4 + x; |
|
S3PaletteIndex ret = { 0, 0 }; |
|
|
|
if ( format == IMAGE_FORMAT_DXT1 ) |
|
{ |
|
const S3TCBlock_DXT1 *pBlock = reinterpret_cast<const S3TCBlock_DXT1 *>( pS3Block ); |
|
ret.m_ColorIndex = (pBlock->m_PixelBits >> (iQuadPixel << 1)) & 3; |
|
ret.m_AlphaIndex = 0; |
|
} |
|
else |
|
{ |
|
Assert( format == IMAGE_FORMAT_DXT5 ); |
|
|
|
const S3TCBlock_DXT5 *pBlock = reinterpret_cast<const S3TCBlock_DXT5 *>( pS3Block ); |
|
|
|
int64 &alphaBits = *((int64*)pBlock->m_AlphaBits); |
|
ret.m_ColorIndex = (unsigned char)((pBlock->m_PixelBits >> (iQuadPixel << 1)) & 3); |
|
ret.m_AlphaIndex = (unsigned char)((alphaBits >> (iQuadPixel * 3)) & 7); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
|
|
void S3TC_SetPixelPaletteIndex( ImageFormat format, char *pS3Block, int x, int y, S3PaletteIndex iPaletteIndex ) |
|
{ |
|
Assert( x >= 0 && x < 4 ); |
|
Assert( y >= 0 && y < 4 ); |
|
Assert( iPaletteIndex.m_ColorIndex >= 0 && iPaletteIndex.m_ColorIndex < 4 ); |
|
Assert( iPaletteIndex.m_AlphaIndex >= 0 && iPaletteIndex.m_AlphaIndex < 8 ); |
|
|
|
int iQuadPixel = y*4 + x; |
|
int iColorBit = iQuadPixel * 2; |
|
|
|
if ( format == IMAGE_FORMAT_DXT1 ) |
|
{ |
|
S3TCBlock_DXT1 *pBlock = reinterpret_cast<S3TCBlock_DXT1 *>( pS3Block ); |
|
|
|
pBlock->m_PixelBits &= ~( 3 << iColorBit ); |
|
pBlock->m_PixelBits |= (unsigned int)iPaletteIndex.m_ColorIndex << iColorBit; |
|
} |
|
else |
|
{ |
|
Assert( format == IMAGE_FORMAT_DXT5 ); |
|
|
|
S3TCBlock_DXT5 *pBlock = reinterpret_cast<S3TCBlock_DXT5 *>( pS3Block ); |
|
|
|
// Copy the color portion in. |
|
pBlock->m_PixelBits &= ~( 3 << iColorBit ); |
|
pBlock->m_PixelBits |= (unsigned int)iPaletteIndex.m_ColorIndex << iColorBit; |
|
|
|
// Copy the alpha portion in. |
|
WriteBitInt( (char*)pBlock->m_AlphaBits, iQuadPixel*3, 3, iPaletteIndex.m_AlphaIndex ); |
|
} |
|
} |
|
|
|
|
|
const char* S3TC_GetBlock( |
|
const void *pCompressed, |
|
ImageFormat format, |
|
int nBlocksWidth, |
|
int xBlock, |
|
int yBlock ) |
|
{ |
|
int nBytesPerBlock = S3TC_BytesPerBlock( format ); |
|
return &((const char*)pCompressed)[ ((yBlock * nBlocksWidth) + xBlock) * nBytesPerBlock ]; |
|
} |
|
|
|
|
|
char* S3TC_GetBlock( |
|
void *pCompressed, |
|
ImageFormat format, |
|
int nBlocksWidth, |
|
int xBlock, |
|
int yBlock ) |
|
{ |
|
return (char*)S3TC_GetBlock( (const void *)pCompressed, format, nBlocksWidth, xBlock, yBlock ); |
|
} |
|
|
|
|
|
void GenerateRepresentativePalette( |
|
ImageFormat format, |
|
S3RGBA **pOriginals, // Original RGBA colors in the texture. This allows it to avoid doubly compressing. |
|
int nBlocks, |
|
int lPitch, // (in BYTES) |
|
char mergedBlocks[16*MAX_S3TC_BLOCK_BYTES] |
|
) |
|
{ |
|
Error( "GenerateRepresentativePalette: not implemented" ); |
|
#if 0 // this code was ifdefed out. no idea under what circumstances it was meant to be called. |
|
|
|
Assert( nBlocks == 2 || nBlocks == 3 ); |
|
|
|
S3RGBA values[12*4]; |
|
memset( values, 0xFF, sizeof( values ) ); |
|
int width = nBlocks * 4; |
|
for ( int i=0; i < nBlocks; i++ ) |
|
{ |
|
for ( int y=0; y < 4; y++ ) |
|
{ |
|
for ( int x=0; x < 4; x++ ) |
|
{ |
|
int outIndex = y*width+(i*4+x); |
|
values[outIndex] = pOriginals[i][y * (lPitch/4) + x]; |
|
} |
|
} |
|
} |
|
|
|
DDSURFACEDESC descIn; |
|
DDSURFACEDESC descOut; |
|
memset( &descIn, 0, sizeof(descIn) ); |
|
memset( &descOut, 0, sizeof(descOut) ); |
|
|
|
descIn.dwSize = sizeof(descIn); |
|
descIn.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_LPSURFACE | DDSD_PIXELFORMAT; |
|
descIn.dwWidth = width; |
|
descIn.dwHeight = 4; |
|
descIn.lPitch = width * 4; |
|
descIn.lpSurface = values; |
|
descIn.ddpfPixelFormat.dwSize = sizeof( DDPIXELFORMAT ); |
|
|
|
descIn.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; |
|
descIn.ddpfPixelFormat.dwRGBBitCount = 32; |
|
descIn.ddpfPixelFormat.dwRBitMask = 0xff0000; |
|
descIn.ddpfPixelFormat.dwGBitMask = 0x00ff00; |
|
descIn.ddpfPixelFormat.dwBBitMask = 0x0000ff; |
|
descIn.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000; |
|
|
|
descOut.dwSize = sizeof( descOut ); |
|
|
|
float weight[3] = {0.3086f, 0.6094f, 0.0820f}; |
|
|
|
S3TC_BLOCK_WIDTH = nBlocks * 4; |
|
|
|
DWORD encodeFlags = S3TC_ENCODE_RGB_FULL; |
|
if ( format == IMAGE_FORMAT_DXT5 ) |
|
encodeFlags |= S3TC_ENCODE_ALPHA_INTERPOLATED; |
|
|
|
S3TCencode( &descIn, NULL, &descOut, mergedBlocks, encodeFlags, weight ); |
|
|
|
S3TC_BLOCK_WIDTH = 4; |
|
#endif |
|
} |
|
|
|
void S3TC_MergeBlocks( |
|
char **blocks, |
|
S3RGBA **pOriginals, // Original RGBA colors in the texture. This allows it to avoid doubly compressing. |
|
int nBlocks, |
|
int lPitch, // (in BYTES) |
|
ImageFormat format |
|
) |
|
{ |
|
// Figure out a good palette to represent all of these blocks. |
|
char mergedBlocks[16*MAX_S3TC_BLOCK_BYTES]; |
|
GenerateRepresentativePalette( format, pOriginals, nBlocks, lPitch, mergedBlocks ); |
|
|
|
// Build a remap table to remap block 2's colors to block 1's colors. |
|
if ( format == IMAGE_FORMAT_DXT1 ) |
|
{ |
|
// Grab the palette indices that s3tc.lib made for us. |
|
const char *pBase = (const char*)mergedBlocks; |
|
pBase += 4; |
|
|
|
for ( int iBlock=0; iBlock < nBlocks; iBlock++ ) |
|
{ |
|
S3TCBlock_DXT1 *pBlock = ((S3TCBlock_DXT1*)blocks[iBlock]); |
|
|
|
// Remap all of the block's pixels. |
|
for ( int x=0; x < 4; x++ ) |
|
{ |
|
for ( int y=0; y < 4; y++ ) |
|
{ |
|
int iBaseBit = (y*nBlocks*4 + x + iBlock*4) * 2; |
|
|
|
S3PaletteIndex index = {0, 0}; |
|
index.m_ColorIndex = ReadBitInt( pBase, iBaseBit, 2 ); |
|
|
|
S3TC_SetPixelPaletteIndex( format, (char*)pBlock, x, y, index ); |
|
} |
|
} |
|
|
|
// Copy block 1's palette to block 2. |
|
pBlock->m_Ref1 = ((S3TCBlock_DXT1*)mergedBlocks)->m_Ref1; |
|
pBlock->m_Ref2 = ((S3TCBlock_DXT1*)mergedBlocks)->m_Ref2; |
|
} |
|
} |
|
else |
|
{ |
|
Assert( format == IMAGE_FORMAT_DXT5 ); |
|
|
|
// Skip past the alpha palette. |
|
const char *pAlphaPalette = mergedBlocks; |
|
const char *pAlphaBits = mergedBlocks + 2; |
|
|
|
// Skip past the alpha pixel bits and past the color palette. |
|
const char *pColorPalette = pAlphaBits + 6*nBlocks; |
|
const char *pColorBits = pColorPalette + 4; |
|
|
|
for ( int iBlock=0; iBlock < nBlocks; iBlock++ ) |
|
{ |
|
S3TCBlock_DXT5 *pBlock = ((S3TCBlock_DXT5*)blocks[iBlock]); |
|
|
|
// Remap all of the block's pixels. |
|
for ( int x=0; x < 4; x++ ) |
|
{ |
|
for ( int y=0; y < 4; y++ ) |
|
{ |
|
int iBasePixel = (y*nBlocks*4 + x + iBlock*4); |
|
|
|
S3PaletteIndex index; |
|
index.m_ColorIndex = ReadBitInt( pColorBits, iBasePixel * 2, 2 ); |
|
index.m_AlphaIndex = ReadBitInt( pAlphaBits, iBasePixel * 3, 3 ); |
|
|
|
S3TC_SetPixelPaletteIndex( format, (char*)pBlock, x, y, index ); |
|
} |
|
} |
|
|
|
// Copy block 1's palette to block 2. |
|
pBlock->m_AlphaRef[0] = pAlphaPalette[0]; |
|
pBlock->m_AlphaRef[1] = pAlphaPalette[1]; |
|
pBlock->m_Ref1 = *((unsigned short*)pColorPalette); |
|
pBlock->m_Ref2 = *((unsigned short*)(pColorPalette + 2)); |
|
} |
|
} |
|
} |
|
|
|
|
|
S3PaletteIndex S3TC_GetPaletteIndex( |
|
unsigned char *pFaceData, |
|
ImageFormat format, |
|
int imageWidth, |
|
int x, |
|
int y ) |
|
{ |
|
char *pBlock = S3TC_GetBlock( pFaceData, format, imageWidth>>2, x>>2, y>>2 ); |
|
return S3TC_GetPixelPaletteIndex( format, pBlock, x&3, y&3 ); |
|
} |
|
|
|
|
|
void S3TC_SetPaletteIndex( |
|
unsigned char *pFaceData, |
|
ImageFormat format, |
|
int imageWidth, |
|
int x, |
|
int y, |
|
S3PaletteIndex paletteIndex ) |
|
{ |
|
char *pBlock = S3TC_GetBlock( pFaceData, format, imageWidth>>2, x>>2, y>>2 ); |
|
S3TC_SetPixelPaletteIndex( format, pBlock, x&3, y&3, paletteIndex ); |
|
} |
|
|
|
|
|
|
|
|
|
|