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 );
|
|
}
|
|
|
|
|
|
|
|
|