/****************************************************************************** * SAGE - Scalable Adaptive Graphics Environment * * Copyright (C) 2004 Electronic Visualization Laboratory, * University of Illinois at Chicago * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the distribution. * * Neither the name of the University of Illinois at Chicago nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Direct questions, comments etc about SAGE to http://www.evl.uic.edu/cavern/forum/ * *****************************************************************************/ // From: // Real-Time DXT Compression // May 20th 2006 J.M.P. van Waveren // (c) 2006, Id Software, Inc. #include "dxt.h" #include "util.h" #define DXT_INTR 1 void ExtractBlock( const byte *inPtr, int width, byte *colorBlock ); void ExtractBlock_Intrinsics( const byte *inPtr, int width, byte *colorBlock ); void GetMinMaxColors( const byte *colorBlock, byte *minColor, byte *maxColor ); void GetMinMaxColorsByLuminance( const byte *colorBlock, byte *minColor, byte *maxColor ); void GetMinMaxColorsByBBox( const byte *colorBlock, byte *minColor, byte *maxColor ); void GetMinMaxColors_Intrinsics( const byte *colorBlock, byte *minColor, byte *maxColor ); // for DXT5 void GetMinMaxColorsAlpha( byte *colorBlock, byte *minColor, byte *maxColor ); word ColorTo565( const byte *color ); void EmitByte( byte b, byte*& ); void EmitWord( word s, byte*& ); void EmitDoubleWord( dword i, byte*& ); void EmitColorIndices( const byte *colorBlock, const byte *minColor, const byte *maxColor, byte *&outData ); void EmitColorIndicesFast( const byte *colorBlock, const byte *minColor, const byte *maxColor, byte *&outData ); void EmitColorIndices_Intrinsics( const byte *colorBlock, const byte *minColor, const byte *maxColor, byte *&outData); // Emit indices for DXT5 void EmitAlphaIndices( const byte *colorBlock, const byte minAlpha, const byte maxAlpha, byte *&outData); void EmitAlphaIndicesFast( const byte *colorBlock, const byte minAlpha, const byte maxAlpha, byte *&outData); void EmitAlphaIndices_Intrinsics( const byte *colorBlock, const byte minAlpha, const byte maxAlpha, byte *&outData); void CompressImageDXT1( const byte *inBuf, byte *outBuf, int width, int height, int &outputBytes ) { ALIGN16( byte *outData ); ALIGN16( byte block[64] ); ALIGN16( byte minColor[4] ); ALIGN16( byte maxColor[4] ); outData = outBuf; for ( int j = 0; j < height; j += 4, inBuf += width * 4*4 ) { for ( int i = 0; i < width; i += 4 ) { #if defined(DXT_INTR) ExtractBlock_Intrinsics( inBuf + i * 4, width, block ); #else ExtractBlock( inBuf + i * 4, width, block ); #endif #if defined(DXT_INTR) GetMinMaxColors_Intrinsics( block, minColor, maxColor ); #else GetMinMaxColorsByBBox( block, minColor, maxColor ); #endif EmitWord( ColorTo565( maxColor ), outData ); EmitWord( ColorTo565( minColor ), outData ); #if defined(DXT_INTR) EmitColorIndices_Intrinsics( block, minColor, maxColor, outData ); #else EmitColorIndicesFast( block, minColor, maxColor, outData ); #endif } } outputBytes = (int) ( outData - outBuf ); } void RGBAtoYCoCg(const byte *inBuf, byte *outBuf, int width, int height) { for ( int j = 0; j < width*height; j++ ) { byte R = inBuf[j*4+0]; byte G = inBuf[j*4+1]; byte B = inBuf[j*4+2]; byte A = inBuf[j*4+3]; int Co = R-B; int t = B + (Co/2); int Cg = G-t; int Y = t + (Cg/2); Co += 128; Cg += 96; // if (Co < 0 || Co > 255) // fprintf(stderr, "Co: %d\n", Co); // if (Cg < 0 || Cg > 255) // fprintf(stderr, "Cg: %d\n", Cg); // if ( Y < 0 || Y > 255) // fprintf(stderr, "Y: %d\n", Y); if (Co < 0) Co=0; if (Co > 255) Co=255; if (Cg < 0) Cg=0; if (Cg > 255) Cg=255; outBuf[j*4+0] = Co; outBuf[j*4+1] = Cg; outBuf[j*4+2] = 0; outBuf[j*4+3] = Y; } } void CompressImageDXT5YCoCg( const byte *inBuf, byte *outBuf, int width, int height, int &outputBytes ) { byte *tmpBuf; tmpBuf = (byte*)memalign(16, width*height*4); memset(tmpBuf, 0, width*height*4); RGBAtoYCoCg(inBuf, tmpBuf, width, height); CompressImageDXT5(tmpBuf, outBuf, width, height, outputBytes); memfree(tmpBuf); } void CompressImageDXT5( const byte *inBuf, byte *outBuf, int width, int height, int &outputBytes ) { ALIGN16( byte *outData ); ALIGN16( byte block[64] ); ALIGN16( byte minColor[4] ); ALIGN16( byte maxColor[4] ); outData = outBuf; for ( int j = 0; j < height; j += 4, inBuf += width * 4*4 ) { for ( int i = 0; i < width; i += 4 ) { #if defined(DXT_INTR) ExtractBlock_Intrinsics( inBuf + i * 4, width, block ); #else ExtractBlock( inBuf + i * 4, width, block ); #endif #if defined(DXT_INTR) GetMinMaxColors_Intrinsics( block, minColor, maxColor ); #else GetMinMaxColorsAlpha( block, minColor, maxColor ); #endif EmitByte( maxColor[3], outData); EmitByte( minColor[3], outData); #if defined(DXT_INTR) // Not Yet Implemented //EmitAlphaIndices_Intrinsics( block, minColor[3], maxColor[3], outData ); EmitAlphaIndicesFast( block, minColor[3], maxColor[3], outData ); #else EmitAlphaIndicesFast( block, minColor[3], maxColor[3], outData ); #endif EmitWord( ColorTo565( maxColor ), outData); EmitWord( ColorTo565( minColor ), outData); #if defined(DXT_INTR) EmitColorIndices_Intrinsics( block, minColor, maxColor, outData ); #else EmitColorIndicesFast( block, minColor, maxColor, outData ); #endif } } outputBytes = int( outData - outBuf ); } void ExtractBlock( const byte *inPtr, int width, byte *colorBlock ) { for ( int j = 0; j < 4; j++ ) { memcpy( &colorBlock[j*4*4], inPtr, 4*4 ); inPtr += width * 4; } } word ColorTo565( const byte *color ) { return ( ( color[ 0 ] >> 3 ) << 11 ) | ( ( color[ 1 ] >> 2 ) << 5 ) | ( color[ 2 ] >> 3 ); } void EmitByte( byte b, byte *&outData) { outData[0] = b; outData += 1; } void EmitWord( word s, byte *&outData) { outData[0] = ( s >> 0 ) & 255; outData[1] = ( s >> 8 ) & 255; outData += 2; } void EmitDoubleWord( dword i, byte *&outData) { outData[0] = ( i >> 0 ) & 255; outData[1] = ( i >> 8 ) & 255; outData[2] = ( i >> 16 ) & 255; outData[3] = ( i >> 24 ) & 255; outData += 4; } int ColorDistance( const byte *c1, const byte *c2 ) { return ( ( c1[0] - c2[0] ) * ( c1[0] - c2[0] ) ) + ( ( c1[1] - c2[1] ) * ( c1[1] - c2[1] ) ) + ( ( c1[2] - c2[2] ) * ( c1[2] - c2[2] ) ); } void SwapColors( byte *c1, byte *c2 ) { byte tm[3]; memcpy( tm, c1, 3 ); memcpy( c1, c2, 3 ); memcpy( c2, tm, 3 ); } void GetMinMaxColors( const byte *colorBlock, byte *minColor, byte *maxColor ) { int maxDistance = -1; for ( int i = 0; i < 64 - 4; i += 4 ) { for ( int j = i + 4; j < 64; j += 4 ) { int distance = ColorDistance( &colorBlock[i], &colorBlock[j] ); if ( distance > maxDistance ) { maxDistance = distance; memcpy( minColor, colorBlock+i, 3 ); memcpy( maxColor, colorBlock+j, 3 ); } } } if ( ColorTo565( maxColor ) < ColorTo565( minColor ) ) { SwapColors( minColor, maxColor ); } } int ColorLuminance( const byte *color ) { return ( color[0] + color[1] * 2 + color[2] ); } void GetMinMaxColorsByLuminance( const byte *colorBlock, byte *minColor, byte *maxColor ) { int maxLuminance = -1, minLuminance = MAX_INT; for (int i = 0; i < 16; i++ ) { int luminance = ColorLuminance( colorBlock+i*4 ); if ( luminance > maxLuminance ) { maxLuminance = luminance; memcpy( maxColor, colorBlock+i*4, 3 ); } if ( luminance < minLuminance ) { minLuminance = luminance; memcpy( minColor, colorBlock+i*4, 3 ); } } if ( ColorTo565( maxColor ) < ColorTo565( minColor ) ) { SwapColors( minColor, maxColor ); } } void GetMinMaxColorsByBBox( const byte *colorBlock, byte *minColor, byte *maxColor ) { int i; byte inset[3]; minColor[0] = minColor[1] = minColor[2] = 255; maxColor[0] = maxColor[1] = maxColor[2] = 0; for ( i = 0; i < 16; i++ ) { if ( colorBlock[i*4+0] < minColor[0] ) { minColor[0] = colorBlock[i*4+0]; } if ( colorBlock[i*4+1] < minColor[1] ) { minColor[1] = colorBlock[i*4+1]; } if ( colorBlock[i*4+2] < minColor[2] ) { minColor[2] = colorBlock[i*4+2]; } if ( colorBlock[i*4+0] > maxColor[0] ) { maxColor[0] = colorBlock[i*4+0]; } if ( colorBlock[i*4+1] > maxColor[1] ) { maxColor[1] = colorBlock[i*4+1]; } if ( colorBlock[i*4+2] > maxColor[2] ) { maxColor[2] = colorBlock[i*4+2]; } } inset[0] = ( maxColor[0] - minColor[0] ) >> INSET_SHIFT; inset[1] = ( maxColor[1] - minColor[1] ) >> INSET_SHIFT; inset[2] = ( maxColor[2] - minColor[2] ) >> INSET_SHIFT; minColor[0] = ( minColor[0] + inset[0] <= 255 ) ? minColor[0] + inset[0] : 255; minColor[1] = ( minColor[1] + inset[1] <= 255 ) ? minColor[1] + inset[1] : 255; minColor[2] = ( minColor[2] + inset[2] <= 255 ) ? minColor[2] + inset[2] : 255; maxColor[0] = ( maxColor[0] >= inset[0] ) ? maxColor[0] - inset[0] : 0; maxColor[1] = ( maxColor[1] >= inset[1] ) ? maxColor[1] - inset[1] : 0; maxColor[2] = ( maxColor[2] >= inset[2] ) ? maxColor[2] - inset[2] : 0; } // // GetMinMaxColorsAlpha for DXT5 // void GetMinMaxColorsAlpha( byte *colorBlock, byte *minColor, byte *maxColor ) { int i; byte inset[4]; byte y,cg, co, r, g, b; minColor[0] = minColor[1] = minColor[2] = minColor[3] = 255; maxColor[0] = maxColor[1] = maxColor[2] = maxColor[3] = 0; for ( i = 0; i < 16; i++ ) { r = colorBlock[i*4+0]; g = colorBlock[i*4+1]; b = colorBlock[i*4+2]; y = (g>>1) + ((r+b)>>2); cg = g - ((r+b)>>1); co = r - b; colorBlock[i*4+0] = co; colorBlock[i*4+1] = cg; colorBlock[i*4+2] = 0; colorBlock[i*4+3] = y; if ( colorBlock[i*4+0] < minColor[0] ) { minColor[0] = colorBlock[i*4+0]; } if ( colorBlock[i*4+1] < minColor[1] ) { minColor[1] = colorBlock[i*4+1]; } if ( colorBlock[i*4+2] < minColor[2] ) { minColor[2] = colorBlock[i*4+2]; } if ( colorBlock[i*4+3] < minColor[3] ) { minColor[3] = colorBlock[i*4+3]; } if ( colorBlock[i*4+0] > maxColor[0] ) { maxColor[0] = colorBlock[i*4+0]; } if ( colorBlock[i*4+1] > maxColor[1] ) { maxColor[1] = colorBlock[i*4+1]; } if ( colorBlock[i*4+2] > maxColor[2] ) { maxColor[2] = colorBlock[i*4+2]; } if ( colorBlock[i*4+3] > maxColor[3] ) { maxColor[3] = colorBlock[i*4+3]; } } inset[0] = ( maxColor[0] - minColor[0] ) >> INSET_SHIFT; inset[1] = ( maxColor[1] - minColor[1] ) >> INSET_SHIFT; inset[2] = ( maxColor[2] - minColor[2] ) >> INSET_SHIFT; inset[3] = ( maxColor[3] - minColor[3] ) >> INSET_SHIFT; minColor[0] = ( minColor[0] + inset[0] <= 255 ) ? minColor[0] + inset[0] : 255; minColor[1] = ( minColor[1] + inset[1] <= 255 ) ? minColor[1] + inset[1] : 255; minColor[2] = ( minColor[2] + inset[2] <= 255 ) ? minColor[2] + inset[2] : 255; minColor[3] = ( minColor[3] + inset[3] <= 255 ) ? minColor[3] + inset[3] : 255; maxColor[0] = ( maxColor[0] >= inset[0] ) ? maxColor[0] - inset[0] : 0; maxColor[1] = ( maxColor[1] >= inset[1] ) ? maxColor[1] - inset[1] : 0; maxColor[2] = ( maxColor[2] >= inset[2] ) ? maxColor[2] - inset[2] : 0; maxColor[3] = ( maxColor[3] >= inset[3] ) ? maxColor[3] - inset[3] : 0; } void EmitColorIndices( const byte *colorBlock, const byte *minColor, const byte *maxColor, byte *&outData ) { byte colors[4][4]; unsigned int indices[16]; colors[0][0] = ( maxColor[0] & C565_5_MASK ) | ( maxColor[0] >> 5 ); colors[0][1] = ( maxColor[1] & C565_6_MASK ) | ( maxColor[1] >> 6 ); colors[0][2] = ( maxColor[2] & C565_5_MASK ) | ( maxColor[2] >> 5 ); colors[1][0] = ( minColor[0] & C565_5_MASK ) | ( minColor[0] >> 5 ); colors[1][1] = ( minColor[1] & C565_6_MASK ) | ( minColor[1] >> 6 ); colors[1][2] = ( minColor[2] & C565_5_MASK ) | ( minColor[2] >> 5 ); colors[2][0] = ( 2 * colors[0][0] + 1 * colors[1][0] ) / 3; colors[2][1] = ( 2 * colors[0][1] + 1 * colors[1][1] ) / 3; colors[2][2] = ( 2 * colors[0][2] + 1 * colors[1][2] ) / 3; colors[3][0] = ( 1 * colors[0][0] + 2 * colors[1][0] ) / 3; colors[3][1] = ( 1 * colors[0][1] + 2 * colors[1][1] ) / 3; colors[3][2] = ( 1 * colors[0][2] + 2 * colors[1][2] ) / 3; for ( int i = 0; i < 16; i++ ) { unsigned int minDistance = MAX_INT; for ( int j = 0; j < 4; j++ ) { unsigned int dist = ColorDistance( &colorBlock[i*4], &colors[j][0] ); if ( dist < minDistance ) { minDistance = dist; indices[i] = j; } } } dword result = 0; for ( int i = 0; i < 16; i++ ) { result |= ( indices[i] << (unsigned int)( i << 1 ) ); } EmitDoubleWord( result, outData ); } void EmitColorIndicesFast( const byte *colorBlock, const byte *minColor, const byte *maxColor, byte *&outData ) { word colors[4][4]; dword result = 0; colors[0][0] = ( maxColor[0] & C565_5_MASK ) | ( maxColor[0] >> 5 ); colors[0][1] = ( maxColor[1] & C565_6_MASK ) | ( maxColor[1] >> 6 ); colors[0][2] = ( maxColor[2] & C565_5_MASK ) | ( maxColor[2] >> 5 ); colors[1][0] = ( minColor[0] & C565_5_MASK ) | ( minColor[0] >> 5 ); colors[1][1] = ( minColor[1] & C565_6_MASK ) | ( minColor[1] >> 6 ); colors[1][2] = ( minColor[2] & C565_5_MASK ) | ( minColor[2] >> 5 ); colors[2][0] = ( 2 * colors[0][0] + 1 * colors[1][0] ) / 3; colors[2][1] = ( 2 * colors[0][1] + 1 * colors[1][1] ) / 3; colors[2][2] = ( 2 * colors[0][2] + 1 * colors[1][2] ) / 3; colors[3][0] = ( 1 * colors[0][0] + 2 * colors[1][0] ) / 3; colors[3][1] = ( 1 * colors[0][1] + 2 * colors[1][1] ) / 3; colors[3][2] = ( 1 * colors[0][2] + 2 * colors[1][2] ) / 3; for ( int i = 15; i >= 0; i-- ) { int c0 = colorBlock[i*4+0]; int c1 = colorBlock[i*4+1]; int c2 = colorBlock[i*4+2]; int d0 = abs( colors[0][0] - c0 ) + abs( colors[0][1] - c1 ) + abs( colors[0][2] - c2 ); int d1 = abs( colors[1][0] - c0 ) + abs( colors[1][1] - c1 ) + abs( colors[1][2] - c2 ); int d2 = abs( colors[2][0] - c0 ) + abs( colors[2][1] - c1 ) + abs( colors[2][2] - c2 ); int d3 = abs( colors[3][0] - c0 ) + abs( colors[3][1] - c1 ) + abs( colors[3][2] - c2 ); int b0 = d0 > d3; int b1 = d1 > d2; int b2 = d0 > d2; int b3 = d1 > d3; int b4 = d2 > d3; int x0 = b1 & b2; int x1 = b0 & b3; int x2 = b0 & b4; result |= ( x2 | ( ( x0 | x1 ) << 1 ) ) << ( i << 1 ); } EmitDoubleWord( result, outData ); } // // Emit indices for DXT5 // void EmitAlphaIndices( const byte *colorBlock, const byte minAlpha, const byte maxAlpha, byte *&outData ) { byte indices[16]; byte alphas[8]; alphas[0] = maxAlpha; alphas[1] = minAlpha; alphas[2] = ( 6 * maxAlpha + 1 * minAlpha ) / 7; alphas[3] = ( 5 * maxAlpha + 2 * minAlpha ) / 7; alphas[4] = ( 4 * maxAlpha + 3 * minAlpha ) / 7; alphas[5] = ( 3 * maxAlpha + 4 * minAlpha ) / 7; alphas[6] = ( 2 * maxAlpha + 5 * minAlpha ) / 7; alphas[7] = ( 1 * maxAlpha + 6 * minAlpha ) / 7; colorBlock += 3; for ( int i = 0; i < 16; i++ ) { int minDistance = MAX_INT; byte a = colorBlock[i*4]; for ( int j = 0; j < 8; j++ ) { int dist = abs( a - alphas[j] ); if ( dist < minDistance ) { minDistance = dist; indices[i] = j; } } } EmitByte( (indices[ 0] >> 0) | (indices[ 1] << 3) | (indices[ 2] << 6) , outData); EmitByte( (indices[ 2] >> 2) | (indices[ 3] << 1) | (indices[ 4] << 4) | (indices[ 5] << 7) , outData); EmitByte( (indices[ 5] >> 1) | (indices[ 6] << 2) | (indices[ 7] << 5) , outData); EmitByte( (indices[ 8] >> 0) | (indices[ 9] << 3) | (indices[10] << 6) , outData); EmitByte( (indices[10] >> 2) | (indices[11] << 1) | (indices[12] << 4) | (indices[13] << 7) , outData); EmitByte( (indices[13] >> 1) | (indices[14] << 2) | (indices[15] << 5) , outData); } void EmitAlphaIndicesFast( const byte *colorBlock, const byte minAlpha, const byte maxAlpha, byte *&outData ) { //assert( maxAlpha > minAlpha ); byte indices[16]; byte mid = ( maxAlpha - minAlpha ) / ( 2 * 7 ); byte ab1 = minAlpha + mid; byte ab2 = ( 6 * maxAlpha + 1 * minAlpha ) / 7 + mid; byte ab3 = ( 5 * maxAlpha + 2 * minAlpha ) / 7 + mid; byte ab4 = ( 4 * maxAlpha + 3 * minAlpha ) / 7 + mid; byte ab5 = ( 3 * maxAlpha + 4 * minAlpha ) / 7 + mid; byte ab6 = ( 2 * maxAlpha + 5 * minAlpha ) / 7 + mid; byte ab7 = ( 1 * maxAlpha + 6 * minAlpha ) / 7 + mid; colorBlock += 3; for ( int i = 0; i < 16; i++ ) { byte a = colorBlock[i*4]; int b1 = ( a <= ab1 ); int b2 = ( a <= ab2 ); int b3 = ( a <= ab3 ); int b4 = ( a <= ab4 ); int b5 = ( a <= ab5 ); int b6 = ( a <= ab6 ); int b7 = ( a <= ab7 ); int index = ( b1 + b2 + b3 + b4 + b5 + b6 + b7 + 1 ) & 7; indices[i] = index ^ ( 2 > index ); } EmitByte( (indices[ 0] >> 0) | (indices[ 1] << 3) | (indices[ 2] << 6) , outData); EmitByte( (indices[ 2] >> 2) | (indices[ 3] << 1) | (indices[ 4] << 4) | (indices[ 5] << 7) , outData); EmitByte( (indices[ 5] >> 1) | (indices[ 6] << 2) | (indices[ 7] << 5) , outData); EmitByte( (indices[ 8] >> 0) | (indices[ 9] << 3) | (indices[10] << 6) , outData); EmitByte( (indices[10] >> 2) | (indices[11] << 1) | (indices[12] << 4) | (indices[13] << 7) , outData); EmitByte( (indices[13] >> 1) | (indices[14] << 2) | (indices[15] << 5) , outData); } double ComputeError( const byte *original, const byte *dxt, int width, int height) { // Compute RMS error double error; int s, d; error = 0.0; // dxt: pointer to data read back from the framebuffer. RGB data upside down. for (int i=0; i