//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #ifdef _WIN32 #include #endif #include "bitmap/imageformat.h" #include "basetypes.h" #include "tier0/dbg.h" #include #include #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( 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( 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( 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( 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( 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 ); }