doom3-bfg/neo/renderer/DXT/DXTEncoder.cpp

5213 lines
134 KiB
C++
Raw Normal View History

2012-11-26 18:58:24 +00:00
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
2012-11-26 18:58:24 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
2012-11-26 18:58:24 +00:00
Doom 3 BFG Edition Source Code 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.
Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
/*
================================================================================================
Contains the DxtEncoder implementation.
================================================================================================
*/
#pragma hdrstop
#include "DXTCodec_local.h"
#include "DXTCodec.h"
#define INSET_COLOR_SHIFT 4 // inset the bounding box with ( range >> shift )
#define INSET_ALPHA_SHIFT 5 // inset alpha channel
#define C565_5_MASK 0xF8 // 0xFF minus last three bits
#define C565_6_MASK 0xFC // 0xFF minus last two bits
#define NVIDIA_7X_HARDWARE_BUG_FIX // keep the DXT5 colors sorted as: max, min
typedef uint16 word;
typedef uint32 dword;
/*
========================
idDxtEncoder::NV4XHardwareBugFix
========================
*/
void idDxtEncoder::NV4XHardwareBugFix( byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
int minq = ( ( minColor[0] << 16 ) | ( minColor[1] << 8 ) | minColor[2] ) & 0x00F8FCF8;
int maxq = ( ( maxColor[0] << 16 ) | ( maxColor[1] << 8 ) | maxColor[2] ) & 0x00F8FCF8;
int mask = -( minq > maxq ) & 0x00FFFFFF;
int min = *( int* )minColor;
int max = *( int* )maxColor;
2012-11-26 18:58:24 +00:00
min ^= max;
max ^= ( min & mask );
min ^= max;
*( int* )minColor = min;
*( int* )maxColor = max;
2012-11-26 18:58:24 +00:00
}
/*
========================
idDxtEncoder::HasConstantValuePer4x4Block
========================
*/
bool idDxtEncoder::HasConstantValuePer4x4Block( const byte* inBuf, int width, int height, int channel ) const
{
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
byte value = inBuf[channel];
for( int k = 0; k < height; k++ )
{
for( int l = 0; l < width; l++ )
{
if( inBuf[( k * width + l ) * 4 + channel] != value )
{
2012-11-26 18:58:24 +00:00
return false;
}
}
}
return true;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
const byte* inPtr = inBuf + i * 4;
2012-11-26 18:58:24 +00:00
byte value = inPtr[channel];
for( int k = 0; k < 4; k++ )
{
for( int l = 0; l < 4; l++ )
{
if( inPtr[( k * width + l ) * 4 + channel] != value )
{
2012-11-26 18:58:24 +00:00
return false;
}
}
}
}
inBuf += srcPadding;
}
return true;
}
/*
========================
idDxtEncoder::WriteTinyColorDXT1
========================
*/
void idDxtEncoder::WriteTinyColorDXT1( const byte* inBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
int numBlocks = ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 );
int stride = ( ( width * height ) / numBlocks ) * 4; // number of bytes from one block to the next
// example: 2x8 pixels
// numBlocks = 2
// stride = 32 bytes (8 pixels)
for( int i = 0; i < numBlocks; i++ )
{
2012-11-26 18:58:24 +00:00
// FIXME: This just emits a fake block based on the color at position 0,0
EmitUShort( ColorTo565( inBuf ) );
EmitUShort( 0 ); // dummy, never used
EmitUInt( 0 ); // 4 color index bytes all use the first color
2012-11-26 18:58:24 +00:00
inBuf += stride;
}
}
/*
========================
idDxtEncoder::WriteTinyColorDXT5
========================
*/
void idDxtEncoder::WriteTinyColorDXT5( const byte* inBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
int numBlocks = ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 );
int stride = ( ( width * height ) / numBlocks ) * 4; // number of bytes from one block to the next
// example: 2x8 pixels
// numBlocks = 2
// stride = 32 bytes (8 pixels)
for( int i = 0; i < numBlocks; i++ )
{
2012-11-26 18:58:24 +00:00
// FIXME: This just emits a fake block based on the color at position 0,0
EmitByte( inBuf[3] );
EmitByte( 0 ); // dummy, never used
EmitByte( 0 ); // 6 alpha index bytes all use the first alpha
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
2012-11-26 18:58:24 +00:00
EmitUShort( ColorTo565( inBuf ) );
EmitUShort( 0 ); // dummy, never used
EmitUInt( 0 ); // 4 color index bytes all use the first color
2012-11-26 18:58:24 +00:00
inBuf += stride;
}
}
/*
========================
idDxtEncoder::WriteTinyColorCTX1DXT5A
========================
*/
void idDxtEncoder::WriteTinyColorCTX1DXT5A( const byte* inBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
int numBlocks = ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 );
int stride = ( ( width * height ) / numBlocks ) * 4; // number of bytes from one block to the next
// example: 2x8 pixels
// numBlocks = 2
// stride = 32 bytes (8 pixels)
for( int i = 0; i < numBlocks; i++ )
{
2012-11-26 18:58:24 +00:00
// FIXME: This just emits a fake block based on the color at position 0,0
EmitByte( inBuf[0] );
EmitByte( inBuf[1] );
EmitByte( inBuf[0] );
EmitByte( inBuf[1] );
EmitUInt( 0 ); // 4 color index bytes all use the first color
2012-11-26 18:58:24 +00:00
EmitByte( inBuf[3] );
EmitByte( 0 ); // dummy, never used
EmitByte( 0 ); // 6 alpha index bytes all use the first alpha
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
2012-11-26 18:58:24 +00:00
inBuf += stride;
}
}
/*
========================
idDxtEncoder::WriteTinyNormalMapDXT5
========================
*/
void idDxtEncoder::WriteTinyNormalMapDXT5( const byte* inBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
int numBlocks = ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 );
int stride = ( ( width * height ) / numBlocks ) * 4; // number of bytes from one block to the next
// example: 2x8 pixels
// numBlocks = 2
// stride = 32 bytes (8 pixels)
for( int i = 0; i < numBlocks; i++ )
{
2012-11-26 18:58:24 +00:00
// FIXME: This just emits a fake block based on the normal at position 0,0
EmitByte( inBuf[3] );
EmitByte( 0 ); // dummy, never used
EmitByte( 0 ); // 6 alpha index bytes all use the first alpha
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
2012-11-26 18:58:24 +00:00
EmitUShort( ColorTo565( inBuf[0], inBuf[1], inBuf[2] ) );
EmitUShort( 0 ); // dummy, never used
EmitUInt( 0 ); // 4 color index bytes all use the first color
2012-11-26 18:58:24 +00:00
inBuf += stride;
}
}
/*
========================
idDxtEncoder::WriteTinyNormalMapDXN
========================
*/
void idDxtEncoder::WriteTinyNormalMapDXN( const byte* inBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
int numBlocks = ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 );
int stride = ( ( width * height ) / numBlocks ) * 4; // number of bytes from one block to the next
// example: 2x8 pixels
// numBlocks = 2
// stride = 32 bytes (8 pixels)
for( int i = 0; i < numBlocks; i++ )
{
2012-11-26 18:58:24 +00:00
// FIXME: This just emits a fake block based on the normal at position 0,0
EmitByte( inBuf[0] );
EmitByte( 0 ); // dummy, never used
EmitByte( 0 ); // 6 alpha index bytes all use the first alpha
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
2012-11-26 18:58:24 +00:00
EmitByte( inBuf[1] );
EmitByte( 0 ); // dummy, never used
EmitByte( 0 ); // 6 alpha index bytes all use the first alpha
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
2012-11-26 18:58:24 +00:00
inBuf += stride;
}
}
/*
========================
idDxtEncoder::WriteTinyDXT5A
========================
*/
void idDxtEncoder::WriteTinyDXT5A( const byte* inBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
int numBlocks = ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 );
int stride = ( ( width * height ) / numBlocks ) * 4; // number of bytes from one block to the next
// example: 2x8 pixels
// numBlocks = 2
// stride = 32 bytes (8 pixels)
for( int i = 0; i < numBlocks; i++ )
{
2012-11-26 18:58:24 +00:00
// FIXME: This just emits a fake block based on the normal at position 0,0
EmitByte( inBuf[0] );
EmitByte( 0 ); // dummy, never used
EmitByte( 0 ); // 6 alpha index bytes all use the first alpha
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
EmitByte( 0 );
2012-11-26 18:58:24 +00:00
inBuf += stride;
}
}
/*
========================
idDxtEncoder::ExtractBlock
params: inPtr - input image, 4 bytes per pixel
paramO: colorBlock - 4*4 output tile, 4 bytes per pixel
========================
*/
ID_INLINE void idDxtEncoder::ExtractBlock( const byte* inPtr, int width, byte* colorBlock ) const
{
for( int j = 0; j < 4; j++ )
{
memcpy( &colorBlock[j * 4 * 4], inPtr, 4 * 4 );
2012-11-26 18:58:24 +00:00
inPtr += width * 4;
}
}
/*
========================
SwapColors
========================
*/
void SwapColors( byte* c1, byte* c2 )
{
2012-11-26 18:58:24 +00:00
byte tm[3];
memcpy( tm, c1, 3 );
memcpy( c1, c2, 3 );
memcpy( c2, tm, 3 );
}
/*
========================
idDxtEncoder::GetMinMaxColorsMaxDist
Finds the two RGB colors in a 4x4 block furthest apart. Also finds the two alpha values
2012-11-26 18:58:24 +00:00
furthest apart.
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte min color
paramO: maxColor - 4 byte max color
========================
*/
void idDxtEncoder::GetMinMaxColorsMaxDist( const byte* colorBlock, byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
int maxDistC = -1;
int maxDistA = -1;
for( int i = 0; i < 64 - 4; i += 4 )
{
for( int j = i + 4; j < 64; j += 4 )
{
2012-11-26 18:58:24 +00:00
int dc = ColorDistance( &colorBlock[i], &colorBlock[j] );
if( dc > maxDistC )
{
2012-11-26 18:58:24 +00:00
maxDistC = dc;
memcpy( minColor, colorBlock + i, 3 );
memcpy( maxColor, colorBlock + j, 3 );
2012-11-26 18:58:24 +00:00
}
int da = AlphaDistance( colorBlock[i + 3], colorBlock[j + 3] );
if( da > maxDistA )
{
2012-11-26 18:58:24 +00:00
maxDistA = da;
minColor[3] = colorBlock[i + 3];
maxColor[3] = colorBlock[j + 3];
2012-11-26 18:58:24 +00:00
}
}
}
if( maxColor[0] < minColor[0] )
{
2012-11-26 18:58:24 +00:00
SwapColors( minColor, maxColor );
}
}
/*
========================
idDxtEncoder::GetMinMaxColorsLuminance
Finds the two RGB colors in a 4x4 block furthest apart based on luminance. Also finds the two
2012-11-26 18:58:24 +00:00
alpha values furthest apart.
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte min color
paramO: maxColor - 4 byte max color
========================
*/
void idDxtEncoder::GetMinMaxColorsLuminance( const byte* colorBlock, byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
int maxLumC = 0, minLumC = 256 * 4;
int maxAlpha = 0, minAlpha = 256 * 4;
for( int i = 0; i < 16; i++ )
{
int luminance = colorBlock[i * 4 + 0] + colorBlock[i * 4 + 1] * 2 + colorBlock[i * 4 + 2];
if( luminance > maxLumC )
{
2012-11-26 18:58:24 +00:00
maxLumC = luminance;
memcpy( maxColor, colorBlock + i * 4, 3 );
2012-11-26 18:58:24 +00:00
}
if( luminance < minLumC )
{
2012-11-26 18:58:24 +00:00
minLumC = luminance;
memcpy( minColor, colorBlock + i * 4, 3 );
2012-11-26 18:58:24 +00:00
}
int alpha = colorBlock[i * 4 + 3];
if( alpha > maxAlpha )
{
2012-11-26 18:58:24 +00:00
maxAlpha = alpha;
maxColor[3] = ( byte )alpha;
2012-11-26 18:58:24 +00:00
}
if( alpha < minAlpha )
{
2012-11-26 18:58:24 +00:00
minAlpha = alpha;
minColor[3] = ( byte )alpha;
2012-11-26 18:58:24 +00:00
}
}
if( maxColor[0] < minColor[0] )
{
2012-11-26 18:58:24 +00:00
SwapColors( minColor, maxColor );
}
}
/*
========================
idDxtEncoder::GetSquareAlphaError
params: colorBlock - 16 pixel block for which to find color indexes
paramO: minAlpha - Min alpha found
paramO: maxAlpha - Max alpha found
return: 4 byte color index block
========================
*/
int idDxtEncoder::GetSquareAlphaError( const byte* colorBlock, const int alphaOffset, const byte minAlpha, const byte maxAlpha, int lastError ) const
{
2012-11-26 18:58:24 +00:00
int i, j;
byte alphas[8];
2012-11-26 18:58:24 +00:00
alphas[0] = maxAlpha;
alphas[1] = minAlpha;
if( maxAlpha > minAlpha )
{
2012-11-26 18:58:24 +00:00
alphas[2] = ( 6 * alphas[0] + 1 * 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] = ( 1 * alphas[0] + 6 * alphas[1] ) / 7;
}
else
{
2012-11-26 18:58:24 +00:00
alphas[2] = ( 4 * alphas[0] + 1 * alphas[1] ) / 5;
alphas[3] = ( 3 * alphas[0] + 2 * alphas[1] ) / 5;
alphas[4] = ( 2 * alphas[0] + 3 * alphas[1] ) / 5;
alphas[5] = ( 1 * alphas[0] + 4 * alphas[1] ) / 5;
alphas[6] = 0;
alphas[7] = 255;
}
2012-11-26 18:58:24 +00:00
int error = 0;
for( i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
byte a = colorBlock[i * 4 + alphaOffset];
for( j = 0; j < 8; j++ )
{
2012-11-26 18:58:24 +00:00
unsigned int dist = AlphaDistance( a, alphas[j] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
}
}
error += minDist;
if( error >= lastError )
{
2012-11-26 18:58:24 +00:00
return error;
}
}
2012-11-26 18:58:24 +00:00
return error;
}
/*
========================
idDxtEncoder::GetMinMaxAlphaHQ
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte min color found
paramO: maxColor - 4 byte max color found
========================
*/
int idDxtEncoder::GetMinMaxAlphaHQ( const byte* colorBlock, const int alphaOffset, byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
int i, j;
byte alphaMin, alphaMax;
int error, bestError = MAX_TYPE( int );
2012-11-26 18:58:24 +00:00
alphaMin = 255;
alphaMax = 0;
2012-11-26 18:58:24 +00:00
// get alpha min / max
for( i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + alphaOffset] < alphaMin )
{
alphaMin = colorBlock[i * 4 + alphaOffset];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + alphaOffset] > alphaMax )
{
alphaMax = colorBlock[i * 4 + alphaOffset];
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
const int ALPHA_EXPAND = 32;
2012-11-26 18:58:24 +00:00
alphaMin = ( alphaMin <= ALPHA_EXPAND ) ? 0 : alphaMin - ALPHA_EXPAND;
alphaMax = ( alphaMax >= 255 - ALPHA_EXPAND ) ? 255 : alphaMax + ALPHA_EXPAND;
for( i = alphaMin; i <= alphaMax; i++ )
{
for( j = alphaMax; j >= i; j-- )
{
error = GetSquareAlphaError( colorBlock, alphaOffset, ( byte )i, ( byte )j, bestError );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
minColor[alphaOffset] = ( byte )i;
maxColor[alphaOffset] = ( byte )j;
2012-11-26 18:58:24 +00:00
}
error = GetSquareAlphaError( colorBlock, alphaOffset, ( byte )j, ( byte )i, bestError );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
minColor[alphaOffset] = ( byte )i;
maxColor[alphaOffset] = ( byte )j;
2012-11-26 18:58:24 +00:00
}
}
}
2012-11-26 18:58:24 +00:00
return bestError;
}
/*
========================
idDxtEncoder::GetSquareColorsError
params: colorBlock - 16 pixel block for which to find color indexes
paramO: color0 - 4 byte min color found
paramO: color1 - 4 byte max color found
return: 4 byte color index block
========================
*/
int idDxtEncoder::GetSquareColorsError( const byte* colorBlock, const unsigned short color0, const unsigned short color1, int lastError ) const
{
2012-11-26 18:58:24 +00:00
int i, j;
byte colors[4][4];
2012-11-26 18:58:24 +00:00
ColorFrom565( color0, colors[0] );
ColorFrom565( color1, colors[1] );
if( color0 > color1 )
{
2012-11-26 18:58:24 +00:00
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;
}
else
{
2012-11-26 18:58:24 +00:00
colors[2][0] = ( 1 * colors[0][0] + 1 * colors[1][0] ) / 2;
colors[2][1] = ( 1 * colors[0][1] + 1 * colors[1][1] ) / 2;
colors[2][2] = ( 1 * colors[0][2] + 1 * colors[1][2] ) / 2;
colors[3][0] = 0;
colors[3][1] = 0;
colors[3][2] = 0;
}
2012-11-26 18:58:24 +00:00
int error = 0;
for( i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
for( j = 0; j < 4; j++ )
{
unsigned int dist = ColorDistance( &colorBlock[i * 4], &colors[j][0] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
}
}
// accumulated error
error += minDist;
if( error > lastError )
{
2012-11-26 18:58:24 +00:00
return error;
}
}
return error;
}
/*
========================
idDxtEncoder::GetSquareNormalYError
params: colorBlock - 16 pixel block for which to find color indexes
paramO: color0 - 4 byte min color found
paramO: color1 - 4 byte max color found
return: 4 byte color index block
========================
*/
int idDxtEncoder::GetSquareNormalYError( const byte* colorBlock, const unsigned short color0, const unsigned short color1, int lastError, int scale ) const
{
2012-11-26 18:58:24 +00:00
int i, j;
byte colors[4][4];
2012-11-26 18:58:24 +00:00
ColorFrom565( color0, colors[0] );
ColorFrom565( color1, colors[1] );
if( color0 > color1 )
{
2012-11-26 18:58:24 +00:00
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;
}
else
{
2012-11-26 18:58:24 +00:00
colors[2][0] = ( 1 * colors[0][0] + 1 * colors[1][0] ) / 2;
colors[2][1] = ( 1 * colors[0][1] + 1 * colors[1][1] ) / 2;
colors[2][2] = ( 1 * colors[0][2] + 1 * colors[1][2] ) / 2;
colors[3][0] = 0;
colors[3][1] = 0;
colors[3][2] = 0;
}
2012-11-26 18:58:24 +00:00
int error = 0;
for( i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
for( j = 0; j < 4; j++ )
{
float r = ( float ) colorBlock[i * 4 + 1] / scale;
float s = ( float ) colors[j][1] / scale;
2012-11-26 18:58:24 +00:00
unsigned int dist = idMath::Ftoi( ( r - s ) * ( r - s ) );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
}
}
// accumulated error
error += minDist;
if( error > lastError )
{
2012-11-26 18:58:24 +00:00
return error;
}
}
return error;
}
/*
========================
idDxtEncoder::GetMinMaxColorsHQ
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
2012-11-26 18:58:24 +00:00
compress the 4x4 block. Also finds the minimum and maximum alpha values.
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte min color found
paramO: maxColor - 4 byte max color found
========================
*/
int idDxtEncoder::GetMinMaxColorsHQ( const byte* colorBlock, byte* minColor, byte* maxColor, bool noBlack ) const
{
2012-11-26 18:58:24 +00:00
int i;
int i0, i1, i2, j0, j1, j2;
unsigned short minColor565, maxColor565, bestMinColor565, bestMaxColor565;
byte bboxMin[3], bboxMax[3], minAxisDist[3];
int error, bestError = MAX_TYPE( int );
2012-11-26 18:58:24 +00:00
bboxMin[0] = bboxMin[1] = bboxMin[2] = 255;
bboxMax[0] = bboxMax[1] = bboxMax[2] = 0;
2012-11-26 18:58:24 +00:00
// get color bbox
for( i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + 0] < bboxMin[0] )
{
bboxMin[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] < bboxMin[1] )
{
bboxMin[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] < bboxMin[2] )
{
bboxMin[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 0] > bboxMax[0] )
{
bboxMax[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] > bboxMax[1] )
{
bboxMax[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] > bboxMax[2] )
{
bboxMax[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
// decrease range for 565 encoding
bboxMin[0] >>= 3;
bboxMin[1] >>= 2;
bboxMin[2] >>= 3;
bboxMax[0] >>= 3;
bboxMax[1] >>= 2;
bboxMax[2] >>= 3;
2012-11-26 18:58:24 +00:00
// get the minimum distance the end points of the line must be apart along each axis
for( i = 0; i < 3; i++ )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = ( bboxMax[i] - bboxMin[i] );
if( minAxisDist[i] >= 16 )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = minAxisDist[i] * 3 / 4;
}
else if( minAxisDist[i] >= 8 )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = minAxisDist[i] * 2 / 4;
}
else if( minAxisDist[i] >= 4 )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = minAxisDist[i] * 1 / 4;
}
else
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = 0;
}
}
2012-11-26 18:58:24 +00:00
// expand the bounding box
const int C565_BBOX_EXPAND = 1;
2012-11-26 18:58:24 +00:00
bboxMin[0] = ( bboxMin[0] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[0] - C565_BBOX_EXPAND;
bboxMin[1] = ( bboxMin[1] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[1] - C565_BBOX_EXPAND;
bboxMin[2] = ( bboxMin[2] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[2] - C565_BBOX_EXPAND;
bboxMax[0] = ( bboxMax[0] >= ( 255 >> 3 ) - C565_BBOX_EXPAND ) ? ( 255 >> 3 ) : bboxMax[0] + C565_BBOX_EXPAND;
bboxMax[1] = ( bboxMax[1] >= ( 255 >> 2 ) - C565_BBOX_EXPAND ) ? ( 255 >> 2 ) : bboxMax[1] + C565_BBOX_EXPAND;
bboxMax[2] = ( bboxMax[2] >= ( 255 >> 3 ) - C565_BBOX_EXPAND ) ? ( 255 >> 3 ) : bboxMax[2] + C565_BBOX_EXPAND;
2012-11-26 18:58:24 +00:00
bestMinColor565 = 0;
bestMaxColor565 = 0;
for( i0 = bboxMin[0]; i0 <= bboxMax[0]; i0++ )
{
for( j0 = bboxMax[0]; j0 >= bboxMin[0]; j0-- )
{
if( abs( i0 - j0 ) < minAxisDist[0] )
{
2012-11-26 18:58:24 +00:00
continue;
}
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
{
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
{
if( abs( i1 - j1 ) < minAxisDist[1] )
{
2012-11-26 18:58:24 +00:00
continue;
}
for( i2 = bboxMin[2]; i2 <= bboxMax[2]; i2++ )
{
for( j2 = bboxMax[2]; j2 >= bboxMin[2]; j2-- )
{
if( abs( i2 - j2 ) < minAxisDist[2] )
{
2012-11-26 18:58:24 +00:00
continue;
}
minColor565 = ( unsigned short )( ( i0 << 11 ) | ( i1 << 5 ) | ( i2 << 0 ) );
maxColor565 = ( unsigned short )( ( j0 << 11 ) | ( j1 << 5 ) | ( j2 << 0 ) );
if( !noBlack )
{
2012-11-26 18:58:24 +00:00
error = GetSquareColorsError( colorBlock, maxColor565, minColor565, bestError );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
bestMinColor565 = minColor565;
bestMaxColor565 = maxColor565;
}
}
else
{
if( minColor565 <= maxColor565 )
{
2012-11-26 18:58:24 +00:00
SwapValues( minColor565, maxColor565 );
}
}
2012-11-26 18:58:24 +00:00
error = GetSquareColorsError( colorBlock, minColor565, maxColor565, bestError );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
bestMinColor565 = minColor565;
bestMaxColor565 = maxColor565;
}
}
}
}
}
}
}
2012-11-26 18:58:24 +00:00
ColorFrom565( bestMinColor565, minColor );
ColorFrom565( bestMaxColor565, maxColor );
2012-11-26 18:58:24 +00:00
return bestError;
}
/*
========================
idDxtEncoder::GetSquareCTX1Error
params: colorBlock - 16 pixel block for which to find color indexes
paramO: color0 - Min color found
paramO: color1 - Max color found
return: 4 byte color index block
========================
*/
int idDxtEncoder::GetSquareCTX1Error( const byte* colorBlock, const byte* color0, const byte* color1, int lastError ) const
{
2012-11-26 18:58:24 +00:00
int i, j;
byte colors[4][4];
2012-11-26 18:58:24 +00:00
colors[0][0] = color0[0];
colors[0][1] = color0[1];
colors[1][0] = color1[0];
colors[1][1] = color1[1];
2012-11-26 18:58:24 +00:00
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[3][0] = ( 1 * colors[0][0] + 2 * colors[1][0] ) / 3;
colors[3][1] = ( 1 * colors[0][1] + 2 * colors[1][1] ) / 3;
2012-11-26 18:58:24 +00:00
int error = 0;
for( i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
for( j = 0; j < 4; j++ )
{
unsigned int dist = CTX1Distance( &colorBlock[i * 4], &colors[j][0] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
}
}
// accumulated error
error += minDist;
if( error > lastError )
{
2012-11-26 18:58:24 +00:00
return error;
}
}
return error;
}
/*
========================
idDxtEncoder::GetMinMaxCTX1HQ
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
2012-11-26 18:58:24 +00:00
compress the 4x4 block. Also finds the minimum and maximum alpha values.
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte Min color found
paramO: maxColor - 4 byte Max color found
========================
*/
int idDxtEncoder::GetMinMaxCTX1HQ( const byte* colorBlock, byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
int i;
int i0, i1, j0, j1;
byte curMinColor[2], curMaxColor[2];
byte bboxMin[2], bboxMax[2], minAxisDist[2];
int error, bestError = MAX_TYPE( int );
2012-11-26 18:58:24 +00:00
bboxMin[0] = bboxMin[1] = 255;
bboxMax[0] = bboxMax[1] = 0;
2012-11-26 18:58:24 +00:00
// get color bbox
for( i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + 0] < bboxMin[0] )
{
bboxMin[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] < bboxMin[1] )
{
bboxMin[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 0] > bboxMax[0] )
{
bboxMax[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] > bboxMax[1] )
{
bboxMax[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
// get the minimum distance the end points of the line must be apart along each axis
for( i = 0; i < 2; i++ )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = ( bboxMax[i] - bboxMin[i] );
if( minAxisDist[i] >= 64 )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = minAxisDist[i] * 3 / 4;
}
else if( minAxisDist[i] >= 32 )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = minAxisDist[i] * 2 / 4;
}
else if( minAxisDist[i] >= 16 )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = minAxisDist[i] * 1 / 4;
}
else
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = 0;
}
}
2012-11-26 18:58:24 +00:00
// expand the bounding box
const int CXT1_BBOX_EXPAND = 6;
2012-11-26 18:58:24 +00:00
bboxMin[0] = ( bboxMin[0] <= CXT1_BBOX_EXPAND ) ? 0 : bboxMin[0] - CXT1_BBOX_EXPAND;
bboxMin[1] = ( bboxMin[1] <= CXT1_BBOX_EXPAND ) ? 0 : bboxMin[1] - CXT1_BBOX_EXPAND;
bboxMax[0] = ( bboxMax[0] >= 255 - CXT1_BBOX_EXPAND ) ? 255 : bboxMax[0] + CXT1_BBOX_EXPAND;
bboxMax[1] = ( bboxMax[1] >= 255 - CXT1_BBOX_EXPAND ) ? 255 : bboxMax[1] + CXT1_BBOX_EXPAND;
for( i0 = bboxMin[0]; i0 <= bboxMax[0]; i0++ )
{
for( j0 = bboxMax[0]; j0 >= bboxMin[0]; j0-- )
{
if( abs( i0 - j0 ) < minAxisDist[0] )
{
2012-11-26 18:58:24 +00:00
continue;
}
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
{
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
{
if( abs( i1 - j1 ) < minAxisDist[1] )
{
2012-11-26 18:58:24 +00:00
continue;
}
curMinColor[0] = ( byte )i0;
curMinColor[1] = ( byte )i1;
curMaxColor[0] = ( byte )j0;
curMaxColor[1] = ( byte )j1;
2012-11-26 18:58:24 +00:00
error = GetSquareCTX1Error( colorBlock, curMinColor, curMaxColor, bestError );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
memcpy( minColor, curMinColor, 2 );
memcpy( maxColor, curMaxColor, 2 );
}
}
}
}
}
2012-11-26 18:58:24 +00:00
return bestError;
}
/*
========================
idDxtEncoder::GetMinMaxNormalYHQ
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
2012-11-26 18:58:24 +00:00
compress the 4x4 block. Also finds the minimum and maximum alpha values.
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte Min color found
paramO: maxColor - 4 byte Max color found
========================
*/
int idDxtEncoder::GetMinMaxNormalYHQ( const byte* colorBlock, byte* minColor, byte* maxColor, bool noBlack, int scale ) const
{
2012-11-26 18:58:24 +00:00
unsigned short bestMinColor565, bestMaxColor565;
byte bboxMin[3], bboxMax[3];
int error, bestError = MAX_TYPE( int );
2012-11-26 18:58:24 +00:00
bboxMin[1] = 255;
bboxMax[1] = 0;
2012-11-26 18:58:24 +00:00
// get color bbox
for( int i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + 1] < bboxMin[1] )
{
bboxMin[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] > bboxMax[1] )
{
bboxMax[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
// decrease range for 565 encoding
bboxMin[1] >>= 2;
bboxMax[1] >>= 2;
2012-11-26 18:58:24 +00:00
// expand the bounding box
const int C565_BBOX_EXPAND = 1;
2012-11-26 18:58:24 +00:00
bboxMin[1] = ( bboxMin[1] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[1] - C565_BBOX_EXPAND;
bboxMax[1] = ( bboxMax[1] >= ( 255 >> 2 ) - C565_BBOX_EXPAND ) ? ( 255 >> 2 ) : bboxMax[1] + C565_BBOX_EXPAND;
2012-11-26 18:58:24 +00:00
bestMinColor565 = 0;
bestMaxColor565 = 0;
for( int i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
{
for( int j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
{
if( abs( i1 - j1 ) < 0 )
{
2012-11-26 18:58:24 +00:00
continue;
}
unsigned short minColor565 = ( unsigned short )i1 << 5;
unsigned short maxColor565 = ( unsigned short )j1 << 5;
if( !noBlack )
{
2012-11-26 18:58:24 +00:00
error = GetSquareNormalYError( colorBlock, maxColor565, minColor565, bestError, scale );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
bestMinColor565 = minColor565;
bestMaxColor565 = maxColor565;
}
}
else
{
if( minColor565 <= maxColor565 )
{
2012-11-26 18:58:24 +00:00
SwapValues( minColor565, maxColor565 );
}
}
2012-11-26 18:58:24 +00:00
error = GetSquareNormalYError( colorBlock, minColor565, maxColor565, bestError, scale );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
bestMinColor565 = minColor565;
bestMaxColor565 = maxColor565;
}
}
}
2012-11-26 18:58:24 +00:00
ColorFrom565( bestMinColor565, minColor );
ColorFrom565( bestMaxColor565, maxColor );
int bias = colorBlock[0 * 4 + 0];
int size = colorBlock[0 * 4 + 2];
minColor[0] = maxColor[0] = ( byte )bias;
minColor[2] = maxColor[2] = ( byte )size;
2012-11-26 18:58:24 +00:00
return bestError;
}
ALIGN16( static float SIMD_SSE2_float_scale[4] ) = { 2.0f / 255.0f, 2.0f / 255.0f, 2.0f / 255.0f, 2.0f / 255.0f };
ALIGN16( static float SIMD_SSE2_float_descale[4] ) = { 255.0f / 2.0f, 255.0f / 2.0f, 255.0f / 2.0f, 255.0f / 2.0f };
ALIGN16( static float SIMD_SSE2_float_zero[4] ) = { 0.0f, 0.0f, 0.0f, 0.0f };
ALIGN16( static float SIMD_SSE2_float_one[4] ) = { 1.0f, 1.0f, 1.0f, 1.0f };
ALIGN16( static float SIMD_SSE2_float_half[4] ) = { 0.5f, 0.5f, 0.5f, 0.5f };
ALIGN16( static float SIMD_SSE2_float_255[4] ) = { 255.0f, 255.0f, 255.0f, 255.0f };
ALIGN16( static float SIMD_SP_rsqrt_c0[4] ) = { 3.0f, 3.0f, 3.0f, 3.0f };
ALIGN16( static float SIMD_SP_rsqrt_c1[4] ) = { -0.5f, -0.5f, -0.5f, -0.5f };
ALIGN16( static dword SIMD_SSE2_dword_maskFirstThree[4] ) = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 };
ALIGN16( static dword SIMD_SSE2_dword_maskWords[4] ) = { 0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x00000000 };
#define R_SHUFFLE_PS( x, y, z, w ) (( (w) & 3 ) << 6 | ( (z) & 3 ) << 4 | ( (y) & 3 ) << 2 | ( (x) & 3 ))
/*
========================
NormalDistanceDXT1
========================
*/
int NormalDistanceDXT1( const int* vector, const int* normalized )
{
#if defined(_MSC_VER) && defined(_M_IX86)
2012-11-26 18:58:24 +00:00
int result;
__asm
{
2012-11-26 18:58:24 +00:00
mov esi, vector
mov edi, normalized
cvtdq2ps xmm0, [esi]
mulps xmm0, SIMD_SSE2_float_scale
subps xmm0, SIMD_SSE2_float_one
pand xmm0, SIMD_SSE2_dword_maskFirstThree
movaps xmm1, xmm0
mulps xmm1, xmm1
pshufd xmm2, xmm1, R_SHUFFLE_PS( 2, 3, 0, 1 )
addps xmm2, xmm1
pshufd xmm1, xmm2, R_SHUFFLE_PS( 1, 0, 1, 0 )
addps xmm2, xmm1
2012-11-26 18:58:24 +00:00
rsqrtps xmm1, xmm2
mulps xmm2, xmm1
mulps xmm2, xmm1
subps xmm2, SIMD_SP_rsqrt_c0
mulps xmm1, SIMD_SP_rsqrt_c1
mulps xmm2, xmm1
2012-11-26 18:58:24 +00:00
mulps xmm0, xmm2
addps xmm0, SIMD_SSE2_float_one
mulps xmm0, SIMD_SSE2_float_descale
addps xmm0, SIMD_SSE2_float_half
maxps xmm0, SIMD_SSE2_float_zero
minps xmm0, SIMD_SSE2_float_255
cvttps2dq xmm0, xmm0
psubd xmm0, [edi]
pand xmm0, SIMD_SSE2_dword_maskWords
pmullw xmm0, xmm0
pshufd xmm1, xmm0, R_SHUFFLE_PS( 2, 3, 0, 1 )
paddd xmm0, xmm1
pshufd xmm1, xmm0, R_SHUFFLE_PS( 1, 0, 1, 0 )
paddd xmm0, xmm1
movd result, xmm0
}
return result;
#else // not _MSC_VERSION && defined(_M_IX86)
// DG: alternative implementation for non-MSVC builds
return 0; // FIXME: implementation!!
// DG end
#endif // _MSC_VERSION && defined(_M_IX86)
2012-11-26 18:58:24 +00:00
}
/*
========================
NormalDistanceDXT5
========================
*/
int NormalDistanceDXT5( const int* vector, const int* normalized )
{
#if _MSC_VER && defined(_M_IX86)
2012-11-26 18:58:24 +00:00
int result;
__asm
{
2012-11-26 18:58:24 +00:00
mov esi, vector
mov edi, normalized
#if 0 // object-space
pshufd xmm0, [esi], R_SHUFFLE_PS( 0, 1, 3, 2 )
#else
pshufd xmm0, [esi], R_SHUFFLE_PS( 1, 2, 3, 0 )
#endif
cvtdq2ps xmm0, xmm0
mulps xmm0, SIMD_SSE2_float_scale
subps xmm0, SIMD_SSE2_float_one
pand xmm0, SIMD_SSE2_dword_maskFirstThree
movaps xmm1, xmm0
mulps xmm1, xmm1
pshufd xmm2, xmm1, R_SHUFFLE_PS( 2, 3, 0, 1 )
addps xmm2, xmm1
pshufd xmm1, xmm2, R_SHUFFLE_PS( 1, 0, 1, 0 )
addps xmm2, xmm1
2012-11-26 18:58:24 +00:00
rsqrtps xmm1, xmm2
mulps xmm2, xmm1
mulps xmm2, xmm1
subps xmm2, SIMD_SP_rsqrt_c0
mulps xmm1, SIMD_SP_rsqrt_c1
mulps xmm2, xmm1
2012-11-26 18:58:24 +00:00
mulps xmm0, xmm2
addps xmm0, SIMD_SSE2_float_one
mulps xmm0, SIMD_SSE2_float_descale
addps xmm0, SIMD_SSE2_float_half
maxps xmm0, SIMD_SSE2_float_zero
minps xmm0, SIMD_SSE2_float_255
cvttps2dq xmm0, xmm0
#if 0 // object-space
pshufd xmm3, [edi], R_SHUFFLE_PS( 0, 1, 3, 2 )
#else
pshufd xmm3, [edi], R_SHUFFLE_PS( 1, 2, 3, 0 )
#endif
psubd xmm0, xmm3
pand xmm0, SIMD_SSE2_dword_maskWords
pmullw xmm0, xmm0
pshufd xmm1, xmm0, R_SHUFFLE_PS( 2, 3, 0, 1 )
paddd xmm0, xmm1
pshufd xmm1, xmm0, R_SHUFFLE_PS( 1, 0, 1, 0 )
paddd xmm0, xmm1
movd result, xmm0
}
return result;
#else // not _MSC_VER && defined(_M_IX86)
// DG: alternative implementation for non-MSVC builds
return 0; // FIXME: implementation!!
// DG end
#endif // _MSC_VER && defined(_M_IX86)
2012-11-26 18:58:24 +00:00
}
/*
========================
idDxtEncoder::GetSquareNormalsDXT1Error
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: color0 - 4 byte Min color found
paramO: color1 - 4 byte Max color found
return: 4 byte color index block
========================
*/
int idDxtEncoder::GetSquareNormalsDXT1Error( const int* colorBlock, const unsigned short color0, const unsigned short color1, int lastError, unsigned int& colorIndices ) const
{
2012-11-26 18:58:24 +00:00
byte byteColors[2][4];
ALIGN16( int colors[4][4] );
2012-11-26 18:58:24 +00:00
ColorFrom565( color0, byteColors[0] );
ColorFrom565( color1, byteColors[1] );
for( int i = 0; i < 4; i++ )
{
2012-11-26 18:58:24 +00:00
colors[0][i] = byteColors[0][i];
colors[1][i] = byteColors[1][i];
}
if( color0 > color1 )
{
2012-11-26 18:58:24 +00:00
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;
}
else
{
2012-11-26 18:58:24 +00:00
assert( color0 == color1 );
colors[2][0] = ( 1 * colors[0][0] + 1 * colors[1][0] ) / 2;
colors[2][1] = ( 1 * colors[0][1] + 1 * colors[1][1] ) / 2;
colors[2][2] = ( 1 * colors[0][2] + 1 * colors[1][2] ) / 2;
colors[3][0] = 0;
colors[3][1] = 0;
colors[3][2] = 0;
}
2012-11-26 18:58:24 +00:00
int error = 0;
int tempColorIndices[16];
for( int i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
for( int j = 0; j < 4; j++ )
{
unsigned int dist = NormalDistanceDXT1( &colors[j][0], &colorBlock[i * 4] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
tempColorIndices[i] = j;
}
}
// accumulated error
error += minDist;
if( error > lastError )
{
2012-11-26 18:58:24 +00:00
return error;
}
}
2012-11-26 18:58:24 +00:00
colorIndices = 0;
for( int i = 0; i < 16; i++ )
{
colorIndices |= ( tempColorIndices[i] << ( unsigned int )( i << 1 ) );
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
return error;
}
/*
========================
idDxtEncoder::GetMinMaxNormalsDXT1HQ
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
2012-11-26 18:58:24 +00:00
compress the 4x4 block. Also finds the minimum and maximum alpha values.
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte Min color found
paramO: maxColor - 4 byte Max color found
========================
*/
int idDxtEncoder::GetMinMaxNormalsDXT1HQ( const byte* colorBlock, byte* minColor, byte* maxColor, unsigned int& colorIndices, bool noBlack ) const
{
2012-11-26 18:58:24 +00:00
int i;
int i0, i1, i2, j0, j1, j2;
unsigned short bestMinColor565 = 0;
unsigned short bestMaxColor565 = 0;
byte bboxMin[3], bboxMax[3], minAxisDist[3];
int error, bestError = MAX_TYPE( int );
unsigned int tempColorIndices;
ALIGN16( int intColorBlock[16 * 4] );
2012-11-26 18:58:24 +00:00
bboxMin[0] = bboxMin[1] = bboxMin[2] = 128;
bboxMax[0] = bboxMax[1] = bboxMax[2] = 128;
2012-11-26 18:58:24 +00:00
// get color bbox
for( i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + 0] < bboxMin[0] )
{
bboxMin[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] < bboxMin[1] )
{
bboxMin[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] < bboxMin[2] )
{
bboxMin[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 0] > bboxMax[0] )
{
bboxMax[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] > bboxMax[1] )
{
bboxMax[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] > bboxMax[2] )
{
bboxMax[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
}
for( int i = 0; i < 64; i++ )
{
2012-11-26 18:58:24 +00:00
intColorBlock[i] = colorBlock[i];
}
2012-11-26 18:58:24 +00:00
// decrease range for 565 encoding
bboxMin[0] >>= 3;
bboxMin[1] >>= 2;
bboxMin[2] >>= 3;
bboxMax[0] >>= 3;
bboxMax[1] >>= 2;
bboxMax[2] >>= 3;
2012-11-26 18:58:24 +00:00
// get the minimum distance the end points of the line must be apart along each axis
for( i = 0; i < 3; i++ )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = 0;
}
2012-11-26 18:58:24 +00:00
// expand the bounding box
const int C565_BBOX_EXPAND = 2;
2012-11-26 18:58:24 +00:00
bboxMin[0] = ( bboxMin[0] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[0] - C565_BBOX_EXPAND;
bboxMin[1] = ( bboxMin[1] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[1] - C565_BBOX_EXPAND;
bboxMin[2] = ( bboxMin[2] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[2] - C565_BBOX_EXPAND;
bboxMax[0] = ( bboxMax[0] >= ( 255 >> 3 ) - C565_BBOX_EXPAND ) ? ( 255 >> 3 ) : bboxMax[0] + C565_BBOX_EXPAND;
bboxMax[1] = ( bboxMax[1] >= ( 255 >> 2 ) - C565_BBOX_EXPAND ) ? ( 255 >> 2 ) : bboxMax[1] + C565_BBOX_EXPAND;
bboxMax[2] = ( bboxMax[2] >= ( 255 >> 3 ) - C565_BBOX_EXPAND ) ? ( 255 >> 3 ) : bboxMax[2] + C565_BBOX_EXPAND;
for( i0 = bboxMin[0]; i0 <= bboxMax[0]; i0++ )
{
for( j0 = bboxMax[0]; j0 >= bboxMin[0]; j0-- )
{
if( abs( i0 - j0 ) < minAxisDist[0] )
{
2012-11-26 18:58:24 +00:00
continue;
}
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
{
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
{
if( abs( i1 - j1 ) < minAxisDist[1] )
{
2012-11-26 18:58:24 +00:00
continue;
}
for( i2 = bboxMin[2]; i2 <= bboxMax[2]; i2++ )
{
for( j2 = bboxMax[2]; j2 >= bboxMin[2]; j2-- )
{
if( abs( i2 - j2 ) < minAxisDist[2] )
{
2012-11-26 18:58:24 +00:00
continue;
}
unsigned short minColor565 = ( unsigned short )( ( i0 << 11 ) | ( i1 << 5 ) | ( i2 << 0 ) );
unsigned short maxColor565 = ( unsigned short )( ( j0 << 11 ) | ( j1 << 5 ) | ( j2 << 0 ) );
if( !noBlack )
{
2012-11-26 18:58:24 +00:00
error = GetSquareNormalsDXT1Error( intColorBlock, maxColor565, minColor565, bestError, tempColorIndices );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
bestMinColor565 = minColor565;
bestMaxColor565 = maxColor565;
colorIndices = tempColorIndices;
}
}
else
{
if( minColor565 <= maxColor565 )
{
2012-11-26 18:58:24 +00:00
SwapValues( minColor565, maxColor565 );
}
}
2012-11-26 18:58:24 +00:00
error = GetSquareNormalsDXT1Error( intColorBlock, minColor565, maxColor565, bestError, tempColorIndices );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
bestMinColor565 = minColor565;
bestMaxColor565 = maxColor565;
colorIndices = tempColorIndices;
}
}
}
}
}
}
}
2012-11-26 18:58:24 +00:00
ColorFrom565( bestMinColor565, minColor );
ColorFrom565( bestMaxColor565, maxColor );
2012-11-26 18:58:24 +00:00
return bestError;
}
/*
========================
idDxtEncoder::GetSquareNormalsDXT5Error
params: normalBlock - 16 pixel block for which to find normal indexes
paramO: minNormal - Min normal found
paramO: maxNormal - Max normal found
========================
*/
int idDxtEncoder::GetSquareNormalsDXT5Error( const int* normalBlock, const byte* minNormal, const byte* maxNormal, int lastError, unsigned int& colorIndices, byte* alphaIndices ) const
{
2012-11-26 18:58:24 +00:00
byte alphas[8];
byte colors[4][4];
2012-11-26 18:58:24 +00:00
unsigned short smin = ColorTo565( minNormal );
unsigned short smax = ColorTo565( maxNormal );
2012-11-26 18:58:24 +00:00
ColorFrom565( smax, colors[0] );
ColorFrom565( smin, colors[1] );
if( smax > smin )
{
2012-11-26 18:58:24 +00:00
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;
}
else
{
2012-11-26 18:58:24 +00:00
assert( smax == smin );
colors[2][0] = ( 1 * colors[0][0] + 1 * colors[1][0] ) / 2;
colors[2][1] = ( 1 * colors[0][1] + 1 * colors[1][1] ) / 2;
colors[2][2] = ( 1 * colors[0][2] + 1 * colors[1][2] ) / 2;
colors[3][0] = 0;
colors[3][1] = 0;
colors[3][2] = 0;
}
2012-11-26 18:58:24 +00:00
alphas[0] = maxNormal[3];
alphas[1] = minNormal[3];
if( maxNormal[3] > minNormal[3] )
{
2012-11-26 18:58:24 +00:00
alphas[2] = ( 6 * alphas[0] + 1 * 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] = ( 1 * alphas[0] + 6 * alphas[1] ) / 7;
}
else
{
2012-11-26 18:58:24 +00:00
alphas[2] = ( 4 * alphas[0] + 1 * alphas[1] ) / 5;
alphas[3] = ( 3 * alphas[0] + 2 * alphas[1] ) / 5;
alphas[4] = ( 2 * alphas[0] + 3 * alphas[1] ) / 5;
alphas[5] = ( 1 * alphas[0] + 4 * alphas[1] ) / 5;
alphas[6] = 0;
alphas[7] = 255;
}
2012-11-26 18:58:24 +00:00
int error = 0;
int tempColorIndices[16];
int tempAlphaIndices[16];
for( int i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
ALIGN16( int normal[4] );
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
for( int j = 0; j < 4; j++ )
{
2012-11-26 18:58:24 +00:00
normal[0] = colors[j][0];
normal[1] = colors[j][1];
normal[2] = colors[j][2];
for( int k = 0; k < 8; k++ )
{
2012-11-26 18:58:24 +00:00
normal[3] = alphas[k];
unsigned int dist = NormalDistanceDXT5( normal, &normalBlock[i * 4] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
tempColorIndices[i] = j;
tempAlphaIndices[i] = k;
}
}
}
error += minDist;
if( error >= lastError )
{
2012-11-26 18:58:24 +00:00
return error;
}
}
alphaIndices[0] = byte( ( tempAlphaIndices[ 0] >> 0 ) | ( tempAlphaIndices[ 1] << 3 ) | ( tempAlphaIndices[ 2] << 6 ) );
alphaIndices[1] = byte( ( tempAlphaIndices[ 2] >> 2 ) | ( tempAlphaIndices[ 3] << 1 ) | ( tempAlphaIndices[ 4] << 4 ) | ( tempAlphaIndices[ 5] << 7 ) );
alphaIndices[2] = byte( ( tempAlphaIndices[ 5] >> 1 ) | ( tempAlphaIndices[ 6] << 2 ) | ( tempAlphaIndices[ 7] << 5 ) );
alphaIndices[3] = byte( ( tempAlphaIndices[ 8] >> 0 ) | ( tempAlphaIndices[ 9] << 3 ) | ( tempAlphaIndices[10] << 6 ) );
alphaIndices[4] = byte( ( tempAlphaIndices[10] >> 2 ) | ( tempAlphaIndices[11] << 1 ) | ( tempAlphaIndices[12] << 4 ) | ( tempAlphaIndices[13] << 7 ) );
alphaIndices[5] = byte( ( tempAlphaIndices[13] >> 1 ) | ( tempAlphaIndices[14] << 2 ) | ( tempAlphaIndices[15] << 5 ) );
2012-11-26 18:58:24 +00:00
colorIndices = 0;
for( int i = 0; i < 16; i++ )
{
colorIndices |= ( tempColorIndices[i] << ( unsigned int )( i << 1 ) );
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
return error;
}
/*
========================
idDxtEncoder::GetMinMaxNormalsDXT5HQ
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
2012-11-26 18:58:24 +00:00
compress the 4x4 block. Also finds the minimum and maximum alpha values.
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte Min color found
paramO: maxColor - 4 byte Max color found
========================
*/
int idDxtEncoder::GetMinMaxNormalsDXT5HQ( const byte* colorBlock, byte* minColor, byte* maxColor, unsigned int& colorIndices, byte* alphaIndices ) const
{
2012-11-26 18:58:24 +00:00
int i;
int i0, i1, i3, j0, j1, j3;
byte bboxMin[4], bboxMax[4], minAxisDist[4];
byte tmin[4], tmax[4];
int error, bestError = MAX_TYPE( int );
unsigned int tempColorIndices;
byte tempAlphaIndices[6];
ALIGN16( int intColorBlock[16 * 4] );
2012-11-26 18:58:24 +00:00
bboxMin[0] = bboxMin[1] = bboxMin[2] = bboxMin[3] = 255;
bboxMax[0] = bboxMax[1] = bboxMax[2] = bboxMax[3] = 0;
2012-11-26 18:58:24 +00:00
// get color bbox
for( i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + 0] < bboxMin[0] )
{
bboxMin[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] < bboxMin[1] )
{
bboxMin[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] < bboxMin[2] )
{
bboxMin[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 3] < bboxMin[3] )
{
bboxMin[3] = colorBlock[i * 4 + 3];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 0] > bboxMax[0] )
{
bboxMax[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] > bboxMax[1] )
{
bboxMax[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] > bboxMax[2] )
{
bboxMax[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 3] > bboxMax[3] )
{
bboxMax[3] = colorBlock[i * 4 + 3];
2012-11-26 18:58:24 +00:00
}
}
for( int i = 0; i < 64; i++ )
{
2012-11-26 18:58:24 +00:00
intColorBlock[i] = colorBlock[i];
}
2012-11-26 18:58:24 +00:00
// decrease range for 565 encoding
bboxMin[0] >>= 3;
bboxMin[1] >>= 2;
bboxMax[0] >>= 3;
bboxMax[1] >>= 2;
2012-11-26 18:58:24 +00:00
// get the minimum distance the end points of the line must be apart along each axis
for( i = 0; i < 4; i++ )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = 0;
}
2012-11-26 18:58:24 +00:00
// expand the bounding box
const int C565_BBOX_EXPAND = 2;
const int ALPHA_BBOX_EXPAND = 32;
2012-11-26 18:58:24 +00:00
bboxMin[0] = ( bboxMin[0] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[0] - C565_BBOX_EXPAND;
bboxMin[1] = ( bboxMin[1] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[1] - C565_BBOX_EXPAND;
bboxMin[3] = ( bboxMin[3] <= ALPHA_BBOX_EXPAND ) ? 0 : bboxMin[3] - ALPHA_BBOX_EXPAND;
bboxMax[0] = ( bboxMax[0] >= ( 255 >> 3 ) - C565_BBOX_EXPAND ) ? ( 255 >> 3 ) : bboxMax[0] + C565_BBOX_EXPAND;
bboxMax[1] = ( bboxMax[1] >= ( 255 >> 2 ) - C565_BBOX_EXPAND ) ? ( 255 >> 2 ) : bboxMax[1] + C565_BBOX_EXPAND;
bboxMax[3] = ( bboxMax[3] >= ( 255 ) - ALPHA_BBOX_EXPAND ) ? ( 255 ) : bboxMax[3] + ALPHA_BBOX_EXPAND;
for( i0 = bboxMin[0]; i0 <= bboxMax[0]; i0++ )
{
for( j0 = bboxMax[0]; j0 >= bboxMin[0]; j0-- )
{
if( abs( i0 - j0 ) < minAxisDist[0] )
{
2012-11-26 18:58:24 +00:00
continue;
}
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
{
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
{
if( abs( i1 - j1 ) < minAxisDist[1] )
{
2012-11-26 18:58:24 +00:00
continue;
}
tmin[0] = ( byte )j0 << 3;
tmin[1] = ( byte )j1 << 2;
2012-11-26 18:58:24 +00:00
tmin[2] = 0;
tmax[0] = ( byte )i0 << 3;
tmax[1] = ( byte )i1 << 2;
2012-11-26 18:58:24 +00:00
tmax[2] = 0;
for( i3 = bboxMin[3]; i3 <= bboxMax[3]; i3++ )
{
for( j3 = bboxMax[3]; j3 >= bboxMin[3]; j3-- )
{
if( abs( i3 - j3 ) < minAxisDist[3] )
{
2012-11-26 18:58:24 +00:00
continue;
}
tmin[3] = ( byte )j3;
tmax[3] = ( byte )i3;
2012-11-26 18:58:24 +00:00
error = GetSquareNormalsDXT5Error( intColorBlock, tmin, tmax, bestError, tempColorIndices, tempAlphaIndices );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
memcpy( minColor, tmin, 4 );
memcpy( maxColor, tmax, 4 );
colorIndices = tempColorIndices;
memcpy( alphaIndices, tempAlphaIndices, 6 );
}
tmin[3] = ( byte )i3;
tmax[3] = ( byte )j3;
2012-11-26 18:58:24 +00:00
error = GetSquareNormalsDXT5Error( intColorBlock, tmin, tmax, bestError, tempColorIndices, tempAlphaIndices );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
memcpy( minColor, tmin, 4 );
memcpy( maxColor, tmax, 4 );
colorIndices = tempColorIndices;
memcpy( alphaIndices, tempAlphaIndices, 6 );
}
}
}
}
}
}
}
2012-11-26 18:58:24 +00:00
return bestError;
}
/*
========================
idDxtEncoder::GetMinMaxNormalsDXT5HQFast
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
2012-11-26 18:58:24 +00:00
compress the 4x4 block. Also finds the minimum and maximum alpha values.
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte Min color found
paramO: maxColor - 4 byte Max color found
========================
*/
int idDxtEncoder::GetMinMaxNormalsDXT5HQFast( const byte* colorBlock, byte* minColor, byte* maxColor, unsigned int& colorIndices, byte* alphaIndices ) const
{
2012-11-26 18:58:24 +00:00
int i0, i1, i2, i3, j0, j1, j2, j3;
byte bboxMin[4], bboxMax[4], minAxisDist[4];
byte tmin[4], tmax[4];
int error, bestError = MAX_TYPE( int );
unsigned int tempColorIndices;
byte tempAlphaIndices[6];
ALIGN16( int intColorBlock[16 * 4] );
2012-11-26 18:58:24 +00:00
bboxMin[0] = bboxMin[1] = bboxMin[2] = bboxMin[3] = 255;
bboxMax[0] = bboxMax[1] = bboxMax[2] = bboxMax[3] = 0;
2012-11-26 18:58:24 +00:00
// get color bbox
for( int i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + 0] < bboxMin[0] )
{
bboxMin[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] < bboxMin[1] )
{
bboxMin[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] < bboxMin[2] )
{
bboxMin[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 3] < bboxMin[3] )
{
bboxMin[3] = colorBlock[i * 4 + 3];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 0] > bboxMax[0] )
{
bboxMax[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] > bboxMax[1] )
{
bboxMax[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] > bboxMax[2] )
{
bboxMax[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 3] > bboxMax[3] )
{
bboxMax[3] = colorBlock[i * 4 + 3];
2012-11-26 18:58:24 +00:00
}
}
for( int i = 0; i < 64; i++ )
{
2012-11-26 18:58:24 +00:00
intColorBlock[i] = colorBlock[i];
}
2012-11-26 18:58:24 +00:00
// decrease range for 565 encoding
bboxMin[0] >>= 3;
bboxMin[1] >>= 2;
bboxMin[2] >>= 3;
bboxMax[0] >>= 3;
bboxMax[1] >>= 2;
bboxMax[2] >>= 3;
2012-11-26 18:58:24 +00:00
bboxMin[3] = 0;
bboxMax[3] = 255;
2012-11-26 18:58:24 +00:00
// get the minimum distance the end points of the line must be apart along each axis
for( int i = 0; i < 4; i++ )
{
2012-11-26 18:58:24 +00:00
minAxisDist[i] = 0;
}
2012-11-26 18:58:24 +00:00
// expand the bounding box
const int C565_BBOX_EXPAND = 1;
const int ALPHA_BBOX_EXPAND = 128;
2012-11-26 18:58:24 +00:00
#if 0 // object-space
bboxMin[0] = ( bboxMin[0] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[0] - C565_BBOX_EXPAND;
bboxMax[0] = ( bboxMax[0] >= ( 255 >> 3 ) - C565_BBOX_EXPAND ) ? ( 255 >> 3 ) : bboxMax[0] + C565_BBOX_EXPAND;
2012-11-26 18:58:24 +00:00
bboxMin[2] = 0;
bboxMax[2] = 0;
#else
bboxMin[0] = 0;
bboxMax[0] = 0;
bboxMin[2] = ( bboxMin[2] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[2] - C565_BBOX_EXPAND;
bboxMax[2] = ( bboxMax[2] >= ( 255 >> 2 ) - C565_BBOX_EXPAND ) ? ( 255 >> 2 ) : bboxMax[2] + C565_BBOX_EXPAND;
2012-11-26 18:58:24 +00:00
#endif
2012-11-26 18:58:24 +00:00
bboxMin[1] = ( bboxMin[1] <= C565_BBOX_EXPAND ) ? 0 : bboxMin[1] - C565_BBOX_EXPAND;
bboxMax[1] = ( bboxMax[1] >= ( 255 >> 2 ) - C565_BBOX_EXPAND ) ? ( 255 >> 2 ) : bboxMax[1] + C565_BBOX_EXPAND;
2012-11-26 18:58:24 +00:00
bboxMin[3] = ( bboxMin[3] <= ALPHA_BBOX_EXPAND ) ? 0 : bboxMin[3] - ALPHA_BBOX_EXPAND;
bboxMax[3] = ( bboxMax[3] >= ( 255 ) - ALPHA_BBOX_EXPAND ) ? ( 255 ) : bboxMax[3] + ALPHA_BBOX_EXPAND;
for( i0 = bboxMin[0]; i0 <= bboxMax[0]; i0++ )
{
for( j0 = bboxMax[0]; j0 >= bboxMin[0]; j0-- )
{
if( abs( i0 - j0 ) < minAxisDist[0] )
{
2012-11-26 18:58:24 +00:00
continue;
}
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
{
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
{
if( abs( i1 - j1 ) < minAxisDist[1] )
{
2012-11-26 18:58:24 +00:00
continue;
}
for( i2 = bboxMin[2]; i2 <= bboxMax[2]; i2++ )
{
for( j2 = bboxMax[2]; j2 >= bboxMin[2]; j2-- )
{
if( abs( i2 - j2 ) < minAxisDist[2] )
{
2012-11-26 18:58:24 +00:00
continue;
}
unsigned short minColor565 = ( unsigned short )( ( i0 << 11 ) | ( i1 << 5 ) | i2 );
unsigned short maxColor565 = ( unsigned short )( ( j0 << 11 ) | ( j1 << 5 ) | j2 );
if( minColor565 > maxColor565 )
{
2012-11-26 18:58:24 +00:00
SwapValues( minColor565, maxColor565 );
}
2012-11-26 18:58:24 +00:00
error = GetSquareNormalsDXT1Error( intColorBlock, maxColor565, minColor565, bestError, tempColorIndices );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
ColorFrom565( minColor565, minColor );
ColorFrom565( maxColor565, maxColor );
colorIndices = tempColorIndices;
}
}
}
}
}
}
}
2012-11-26 18:58:24 +00:00
bestError = MAX_TYPE( int );
2012-11-26 18:58:24 +00:00
memcpy( tmin, minColor, 4 );
memcpy( tmax, maxColor, 4 );
for( i3 = bboxMin[3]; i3 <= bboxMax[3]; i3++ )
{
for( j3 = bboxMax[3]; j3 >= bboxMin[3]; j3-- )
{
if( abs( i3 - j3 ) < minAxisDist[3] )
{
2012-11-26 18:58:24 +00:00
continue;
}
tmin[3] = ( byte )j3;
tmax[3] = ( byte )i3;
2012-11-26 18:58:24 +00:00
error = GetSquareNormalsDXT5Error( intColorBlock, tmin, tmax, bestError, tempColorIndices, tempAlphaIndices );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
memcpy( minColor, tmin, 4 );
memcpy( maxColor, tmax, 4 );
colorIndices = tempColorIndices;
memcpy( alphaIndices, tempAlphaIndices, 6 );
}
tmin[3] = ( byte )i3;
tmax[3] = ( byte )j3;
2012-11-26 18:58:24 +00:00
error = GetSquareNormalsDXT5Error( intColorBlock, tmin, tmax, bestError, tempColorIndices, tempAlphaIndices );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
memcpy( minColor, tmin, 4 );
memcpy( maxColor, tmax, 4 );
colorIndices = tempColorIndices;
memcpy( alphaIndices, tempAlphaIndices, 6 );
}
}
}
2012-11-26 18:58:24 +00:00
return bestError;
}
/*
========================
idDxtEncoder::FindColorIndices
params: colorBlock - 16 pixel block for which find color indexes
paramO: color0 - Min color found
paramO: color1 - Max color found
return: 4 byte color index block
========================
*/
int idDxtEncoder::FindColorIndices( const byte* colorBlock, const unsigned short color0, const unsigned short color1, unsigned int& result ) const
{
2012-11-26 18:58:24 +00:00
int i, j;
unsigned int indexes[16];
byte colors[4][4];
2012-11-26 18:58:24 +00:00
ColorFrom565( color0, colors[0] );
ColorFrom565( color1, colors[1] );
if( color0 > color1 )
{
2012-11-26 18:58:24 +00:00
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;
}
else
{
2012-11-26 18:58:24 +00:00
colors[2][0] = ( 1 * colors[0][0] + 1 * colors[1][0] ) / 2;
colors[2][1] = ( 1 * colors[0][1] + 1 * colors[1][1] ) / 2;
colors[2][2] = ( 1 * colors[0][2] + 1 * colors[1][2] ) / 2;
colors[3][0] = 0;
colors[3][1] = 0;
colors[3][2] = 0;
}
2012-11-26 18:58:24 +00:00
int error = 0;
for( i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
for( j = 0; j < 4; j++ )
{
unsigned int dist = ColorDistance( &colorBlock[i * 4], &colors[j][0] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
indexes[i] = j;
}
}
// accumulated error
error += minDist;
}
2012-11-26 18:58:24 +00:00
result = 0;
for( i = 0; i < 16; i++ )
{
result |= ( indexes[i] << ( unsigned int )( i << 1 ) );
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
return error;
}
/*
========================
idDxtEncoder::FindAlphaIndices
params: colorBlock - 16 pixel block for which find alpha indexes
paramO: alpha0 - Min alpha found
paramO: alpha1 - Max alpha found
params: rindexes - 6 byte alpha index block
return: error metric for this compression
========================
*/
int idDxtEncoder::FindAlphaIndices( const byte* colorBlock, const int alphaOffset, const byte alpha0, const byte alpha1, byte* rindexes ) const
{
2012-11-26 18:58:24 +00:00
int i, j;
unsigned int indexes[16];
byte alphas[8];
2012-11-26 18:58:24 +00:00
alphas[0] = alpha0;
alphas[1] = alpha1;
if( alpha0 > alpha1 )
{
2012-11-26 18:58:24 +00:00
alphas[2] = ( 6 * alpha0 + 1 * alpha1 ) / 7;
alphas[3] = ( 5 * alpha0 + 2 * alpha1 ) / 7;
alphas[4] = ( 4 * alpha0 + 3 * alpha1 ) / 7;
alphas[5] = ( 3 * alpha0 + 4 * alpha1 ) / 7;
alphas[6] = ( 2 * alpha0 + 5 * alpha1 ) / 7;
alphas[7] = ( 1 * alpha0 + 6 * alpha1 ) / 7;
}
else
{
2012-11-26 18:58:24 +00:00
alphas[2] = ( 4 * alpha0 + 1 * alpha1 ) / 5;
alphas[3] = ( 3 * alpha0 + 2 * alpha1 ) / 5;
alphas[4] = ( 2 * alpha0 + 3 * alpha1 ) / 5;
alphas[5] = ( 1 * alpha0 + 4 * alpha1 ) / 5;
alphas[6] = 0;
alphas[7] = 255;
}
2012-11-26 18:58:24 +00:00
int error = 0;
for( i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
byte a = colorBlock[i * 4 + alphaOffset];
for( j = 0; j < 8; j++ )
{
2012-11-26 18:58:24 +00:00
unsigned int dist = AlphaDistance( a, alphas[j] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
indexes[i] = j;
}
}
error += minDist;
}
rindexes[0] = byte( ( indexes[ 0] >> 0 ) | ( indexes[ 1] << 3 ) | ( indexes[ 2] << 6 ) );
rindexes[1] = byte( ( indexes[ 2] >> 2 ) | ( indexes[ 3] << 1 ) | ( indexes[ 4] << 4 ) | ( indexes[ 5] << 7 ) );
rindexes[2] = byte( ( indexes[ 5] >> 1 ) | ( indexes[ 6] << 2 ) | ( indexes[ 7] << 5 ) );
rindexes[3] = byte( ( indexes[ 8] >> 0 ) | ( indexes[ 9] << 3 ) | ( indexes[10] << 6 ) );
rindexes[4] = byte( ( indexes[10] >> 2 ) | ( indexes[11] << 1 ) | ( indexes[12] << 4 ) | ( indexes[13] << 7 ) );
rindexes[5] = byte( ( indexes[13] >> 1 ) | ( indexes[14] << 2 ) | ( indexes[15] << 5 ) );
2012-11-26 18:58:24 +00:00
return error;
}
/*
========================
idDxtEncoder::FindCTX1Indices
params: colorBlock - 16 pixel block for which find color indexes
paramO: color0 - Min color found
paramO: color1 - Max color found
return: 4 byte color index block
========================
*/
int idDxtEncoder::FindCTX1Indices( const byte* colorBlock, const byte* color0, const byte* color1, unsigned int& result ) const
{
2012-11-26 18:58:24 +00:00
int i, j;
unsigned int indexes[16];
byte colors[4][4];
2012-11-26 18:58:24 +00:00
colors[0][0] = color1[0];
colors[0][1] = color1[1];
colors[1][0] = color0[0];
colors[1][1] = color0[1];
2012-11-26 18:58:24 +00:00
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[3][0] = ( 1 * colors[0][0] + 2 * colors[1][0] ) / 3;
colors[3][1] = ( 1 * colors[0][1] + 2 * colors[1][1] ) / 3;
2012-11-26 18:58:24 +00:00
int error = 0;
for( i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
for( j = 0; j < 4; j++ )
{
unsigned int dist = CTX1Distance( &colorBlock[i * 4], &colors[j][0] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
indexes[i] = j;
}
}
// accumulated error
error += minDist;
}
2012-11-26 18:58:24 +00:00
result = 0;
for( i = 0; i < 16; i++ )
{
result |= ( indexes[i] << ( unsigned int )( i << 1 ) );
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
return error;
}
/*
========================
idDxtEncoder::CompressImageDXT1HQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressImageDXT1HQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
unsigned int colorIndices1;
unsigned int colorIndices2;
byte col1[4];
byte col2[4];
int error1;
int error2;
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorDXT1( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxColorsHQ( block, col1, col2, false );
2012-11-26 18:58:24 +00:00
// Write out color data. Try and find minimum error for the two encoding methods.
unsigned short scol1 = ColorTo565( col1 );
unsigned short scol2 = ColorTo565( col2 );
2012-11-26 18:58:24 +00:00
error1 = FindColorIndices( block, scol1, scol2, colorIndices1 );
error2 = FindColorIndices( block, scol2, scol1, colorIndices2 );
if( error1 < error2 )
{
2012-11-26 18:58:24 +00:00
EmitUShort( scol1 );
EmitUShort( scol2 );
EmitUInt( colorIndices1 );
}
else
{
2012-11-26 18:58:24 +00:00
EmitUShort( scol2 );
EmitUShort( scol1 );
EmitUInt( colorIndices2 );
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r100%%\n" );
}
/*
========================
idDxtEncoder::CompressImageDXT5HQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressImageDXT5HQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
byte alphaIndices1[6];
byte alphaIndices2[6];
unsigned int colorIndices;
byte col1[4];
byte col2[4];
int error1;
int error2;
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorDXT5( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxColorsHQ( block, col1, col2, true );
GetMinMaxAlphaHQ( block, 3, col1, col2 );
2012-11-26 18:58:24 +00:00
// Write out alpha data. Try and find minimum error for the two encoding methods.
error1 = FindAlphaIndices( block, 3, col1[3], col2[3], alphaIndices1 );
error2 = FindAlphaIndices( block, 3, col2[3], col1[3], alphaIndices2 );
if( error1 < error2 )
{
2012-11-26 18:58:24 +00:00
EmitByte( col1[3] );
EmitByte( col2[3] );
EmitByte( alphaIndices1[0] );
EmitByte( alphaIndices1[1] );
EmitByte( alphaIndices1[2] );
EmitByte( alphaIndices1[3] );
EmitByte( alphaIndices1[4] );
EmitByte( alphaIndices1[5] );
}
else
{
2012-11-26 18:58:24 +00:00
EmitByte( col2[3] );
EmitByte( col1[3] );
EmitByte( alphaIndices2[0] );
EmitByte( alphaIndices2[1] );
EmitByte( alphaIndices2[2] );
EmitByte( alphaIndices2[3] );
EmitByte( alphaIndices2[4] );
EmitByte( alphaIndices2[5] );
}
2012-11-26 18:58:24 +00:00
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
NV4XHardwareBugFix( col2, col1 );
#endif
2012-11-26 18:58:24 +00:00
// Write out color data. Always take the path with 4 interpolated values.
unsigned short scol1 = ColorTo565( col1 );
unsigned short scol2 = ColorTo565( col2 );
2012-11-26 18:58:24 +00:00
EmitUShort( scol1 );
EmitUShort( scol2 );
2012-11-26 18:58:24 +00:00
FindColorIndices( block, scol1, scol2, colorIndices );
EmitUInt( colorIndices );
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r100%%\n" );
}
/*
========================
idDxtEncoder::CompressImageCTX1HQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressImageCTX1HQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
unsigned int colorIndices;
byte col1[4];
byte col2[4];
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorCTX1DXT5A( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxCTX1HQ( block, col1, col2 );
2012-11-26 18:58:24 +00:00
EmitByte( col2[0] );
EmitByte( col2[1] );
EmitByte( col1[0] );
EmitByte( col1[1] );
2012-11-26 18:58:24 +00:00
FindCTX1Indices( block, col1, col2, colorIndices );
EmitUInt( colorIndices );
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r100%%\n" );
}
/*
========================
idDxtEncoder::ScaleYCoCg
params: colorBlock - 16 pixel block for which find color indexes
========================
*/
void idDxtEncoder::ScaleYCoCg( byte* colorBlock ) const
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte minColor[4] );
ALIGN16( byte maxColor[4] );
2012-11-26 18:58:24 +00:00
minColor[0] = minColor[1] = minColor[2] = minColor[3] = 255;
maxColor[0] = maxColor[1] = maxColor[2] = maxColor[3] = 0;
for( int i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + 0] < minColor[0] )
{
minColor[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] < minColor[1] )
{
minColor[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 0] > maxColor[0] )
{
maxColor[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] > maxColor[1] )
{
maxColor[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
int m0 = abs( minColor[0] - 128 );
int m1 = abs( minColor[1] - 128 );
int m2 = abs( maxColor[0] - 128 );
int m3 = abs( maxColor[1] - 128 );
if( m1 > m0 ) m0 = m1;
if( m3 > m2 ) m2 = m3;
if( m2 > m0 ) m0 = m2;
2012-11-26 18:58:24 +00:00
const int s0 = 128 / 2 - 1;
const int s1 = 128 / 4 - 1;
2012-11-26 18:58:24 +00:00
int scale = 1 + ( m0 <= s0 ) + 2 * ( m0 <= s1 );
for( int i = 0; i < 16; i++ )
{
colorBlock[i * 4 + 0] = byte( ( colorBlock[i * 4 + 0] - 128 ) * scale + 128 );
colorBlock[i * 4 + 1] = byte( ( colorBlock[i * 4 + 1] - 128 ) * scale + 128 );
colorBlock[i * 4 + 2] = byte( ( scale - 1 ) << 3 );
2012-11-26 18:58:24 +00:00
}
}
/*
========================
idDxtEncoder::CompressYCoCgDXT5HQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressYCoCgDXT5HQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
byte alphaIndices1[6];
byte alphaIndices2[6];
unsigned int colorIndices;
byte col1[4];
byte col2[4];
int error1;
int error2;
2012-11-26 18:58:24 +00:00
assert( HasConstantValuePer4x4Block( inBuf, width, height, 2 ) );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorDXT5( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
ScaleYCoCg( block );
2012-11-26 18:58:24 +00:00
GetMinMaxColorsHQ( block, col1, col2, true );
GetMinMaxAlphaHQ( block, 3, col1, col2 );
2012-11-26 18:58:24 +00:00
// Write out alpha data. Try and find minimum error for the two encoding methods.
error1 = FindAlphaIndices( block, 3, col1[3], col2[3], alphaIndices1 );
error2 = FindAlphaIndices( block, 3, col2[3], col1[3], alphaIndices2 );
if( error1 < error2 )
{
2012-11-26 18:58:24 +00:00
EmitByte( col1[3] );
EmitByte( col2[3] );
EmitByte( alphaIndices1[0] );
EmitByte( alphaIndices1[1] );
EmitByte( alphaIndices1[2] );
EmitByte( alphaIndices1[3] );
EmitByte( alphaIndices1[4] );
EmitByte( alphaIndices1[5] );
}
else
{
2012-11-26 18:58:24 +00:00
EmitByte( col2[3] );
EmitByte( col1[3] );
EmitByte( alphaIndices2[0] );
EmitByte( alphaIndices2[1] );
EmitByte( alphaIndices2[2] );
EmitByte( alphaIndices2[3] );
EmitByte( alphaIndices2[4] );
EmitByte( alphaIndices2[5] );
}
2012-11-26 18:58:24 +00:00
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
NV4XHardwareBugFix( col2, col1 );
#endif
2012-11-26 18:58:24 +00:00
// Write out color data. Always take the path with 4 interpolated values.
unsigned short scol1 = ColorTo565( col1 );
unsigned short scol2 = ColorTo565( col2 );
2012-11-26 18:58:24 +00:00
EmitUShort( scol1 );
EmitUShort( scol2 );
2012-11-26 18:58:24 +00:00
FindColorIndices( block, scol1, scol2, colorIndices );
EmitUInt( colorIndices );
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r100%%\n" );
}
/*
========================
idDxtEncoder::CompressYCoCgCTX1DXT5AHQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressYCoCgCTX1DXT5AHQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
byte alphaIndices1[6];
byte alphaIndices2[6];
unsigned int colorIndices;
byte col1[4];
byte col2[4];
int error1;
int error2;
2012-11-26 18:58:24 +00:00
assert( HasConstantValuePer4x4Block( inBuf, width, height, 2 ) );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorCTX1DXT5A( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxAlphaHQ( block, 3, col1, col2 );
2012-11-26 18:58:24 +00:00
// Write out alpha data. Try and find minimum error for the two encoding methods.
error1 = FindAlphaIndices( block, 3, col1[3], col2[3], alphaIndices1 );
error2 = FindAlphaIndices( block, 3, col2[3], col1[3], alphaIndices2 );
if( error1 < error2 )
{
2012-11-26 18:58:24 +00:00
EmitByte( col1[3] );
EmitByte( col2[3] );
EmitByte( alphaIndices1[0] );
EmitByte( alphaIndices1[1] );
EmitByte( alphaIndices1[2] );
EmitByte( alphaIndices1[3] );
EmitByte( alphaIndices1[4] );
EmitByte( alphaIndices1[5] );
}
else
{
2012-11-26 18:58:24 +00:00
EmitByte( col2[3] );
EmitByte( col1[3] );
EmitByte( alphaIndices2[0] );
EmitByte( alphaIndices2[1] );
EmitByte( alphaIndices2[2] );
EmitByte( alphaIndices2[3] );
EmitByte( alphaIndices2[4] );
EmitByte( alphaIndices2[5] );
}
2012-11-26 18:58:24 +00:00
GetMinMaxCTX1HQ( block, col1, col2 );
2012-11-26 18:58:24 +00:00
EmitByte( col2[0] );
EmitByte( col2[1] );
EmitByte( col1[0] );
EmitByte( col1[1] );
2012-11-26 18:58:24 +00:00
FindCTX1Indices( block, col1, col2, colorIndices );
EmitUInt( colorIndices );
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r100%%\n" );
}
/*
========================
idDxtEncoder::RotateNormalsDXT1
========================
*/
void idDxtEncoder::RotateNormalsDXT1( byte* block ) const
{
2012-11-26 18:58:24 +00:00
byte rotatedBlock[64];
byte col1[4];
byte col2[4];
int bestError = MAX_TYPE( int );
int bestRotation = 0;
for( int i = 0; i < 32; i += 1 )
{
2012-11-26 18:58:24 +00:00
int r = ( i << 3 ) | ( i >> 2 );
float angle = ( r / 255.0f ) * idMath::PI;
float s = sin( angle );
float c = cos( angle );
for( int j = 0; j < 16; j++ )
{
float x = block[j * 4 + 0] / 255.0f * 2.0f - 1.0f;
float y = block[j * 4 + 1] / 255.0f * 2.0f - 1.0f;
2012-11-26 18:58:24 +00:00
float rx = c * x - s * y;
float ry = s * x + c * y;
rotatedBlock[j * 4 + 0] = idMath::Ftob( ( rx + 1.0f ) / 2.0f * 255.0f );
rotatedBlock[j * 4 + 1] = idMath::Ftob( ( ry + 1.0f ) / 2.0f * 255.0f );
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
int error = GetMinMaxColorsHQ( rotatedBlock, col1, col2, true );
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
bestRotation = r;
}
}
2012-11-26 18:58:24 +00:00
float angle = ( bestRotation / 255.0f ) * idMath::PI;
float s = sin( angle );
float c = cos( angle );
for( int j = 0; j < 16; j++ )
{
float x = block[j * 4 + 0] / 255.0f * 2.0f - 1.0f;
float y = block[j * 4 + 1] / 255.0f * 2.0f - 1.0f;
2012-11-26 18:58:24 +00:00
float rx = c * x - s * y;
float ry = s * x + c * y;
block[j * 4 + 0] = idMath::Ftob( ( rx + 1.0f ) / 2.0f * 255.0f );
block[j * 4 + 1] = idMath::Ftob( ( ry + 1.0f ) / 2.0f * 255.0f );
block[j * 4 + 2] = ( byte )bestRotation;
2012-11-26 18:58:24 +00:00
}
}
/*
========================
idDxtEncoder::CompressNormalMapDXT1HQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressNormalMapDXT1HQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
unsigned int colorIndices;
byte col1[4];
byte col2[4];
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorDXT1( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
for( int k = 0; k < 16; k++ )
{
block[k * 4 + 2] = 0;
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
GetMinMaxColorsHQ( block, col1, col2, true );
2012-11-26 18:58:24 +00:00
// Write out color data. Always take the path with 4 interpolated values.
unsigned short scol1 = ColorTo565( col1 );
unsigned short scol2 = ColorTo565( col2 );
2012-11-26 18:58:24 +00:00
EmitUShort( scol1 );
EmitUShort( scol2 );
2012-11-26 18:58:24 +00:00
FindColorIndices( block, scol1, scol2, colorIndices );
EmitUInt( colorIndices );
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r%3d%%", ( j * width + i * 4 ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r100%%\n" );
}
/*
========================
idDxtEncoder::CompressNormalMapDXT1RenormalizeHQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressNormalMapDXT1RenormalizeHQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
unsigned int colorIndices;
byte col1[4];
byte col2[4];
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorDXT1( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
// clear alpha channel
for( int k = 0; k < 16; k++ )
{
block[k * 4 + 3] = 0;
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
GetMinMaxNormalsDXT1HQ( block, col1, col2, colorIndices, true );
2012-11-26 18:58:24 +00:00
// Write out color data. Always take the path with 4 interpolated values.
unsigned short scol1 = ColorTo565( col1 );
unsigned short scol2 = ColorTo565( col2 );
2012-11-26 18:58:24 +00:00
EmitUShort( scol1 );
EmitUShort( scol2 );
EmitUInt( colorIndices );
2012-11-26 18:58:24 +00:00
////idLib::Printf( "\r%3d%%", ( j * width + i * 4 ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
////idLib::Printf( "\r100%%\n" );
}
#define USE_SCALE 1
#define USE_BIAS 1
static int c_blocks;
static int c_scaled;
static int c_scaled2x;
static int c_scaled4x;
static int c_differentBias;
static int c_biasHelped;
/*
========================
idDxtEncoder::BiasScaleNormalY
* scale2x = 33%
* scale4x = 23%
* bias + scale2x = 30%
* bias + scale4x = 55%
========================
*/
void idDxtEncoder::BiasScaleNormalY( byte* colorBlock ) const
{
2012-11-26 18:58:24 +00:00
byte minColor = 255;
byte maxColor = 0;
for( int i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + 1] < minColor )
{
minColor = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] > maxColor )
{
maxColor = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
int bestBias = 128;
int bestRange = Max( abs( minColor - bestBias ), abs( maxColor - bestBias ) );
#if USE_BIAS
for( int i = 0; i < 32; i++ )
{
2012-11-26 18:58:24 +00:00
int bias = ( ( i << 3 ) | ( i >> 2 ) ) - 4;
int range = Max( abs( minColor - bias ), abs( maxColor - bias ) );
if( range < bestRange )
{
2012-11-26 18:58:24 +00:00
bestRange = range;
bestBias = bias;
}
}
#endif
2012-11-26 18:58:24 +00:00
const int s0 = 128 / 2 - 1;
const int s1 = 128 / 4 - 1;
2012-11-26 18:58:24 +00:00
#if USE_SCALE
int scale = 1 + ( bestRange <= s0 ) + 2 * ( bestRange <= s1 );
#else
int scale = 1;
#endif
if( scale == 1 )
{
2012-11-26 18:58:24 +00:00
bestBias = 128;
}
else
{
2012-11-26 18:58:24 +00:00
c_scaled++;
if( scale == 2 ) c_scaled2x++;
if( scale == 4 ) c_scaled4x++;
if( bestBias != 128 )
{
2012-11-26 18:58:24 +00:00
c_differentBias++;
int r = Max( abs( minColor - 128 ), abs( maxColor - 128 ) );
int s = 1 + ( r <= s0 ) + 2 * ( r <= s1 );
if( scale > s )
{
2012-11-26 18:58:24 +00:00
c_biasHelped++;
}
}
}
2012-11-26 18:58:24 +00:00
c_blocks++;
for( int i = 0; i < 16; i++ )
{
colorBlock[i * 4 + 0] = byte( bestBias + 4 );
colorBlock[i * 4 + 1] = byte( ( colorBlock[i * 4 + 1] - bestBias ) * scale + 128 );
colorBlock[i * 4 + 2] = byte( ( scale - 1 ) << 3 );
2012-11-26 18:58:24 +00:00
}
}
/*
========================
idDxtEncoder::RotateNormalsDXT5
========================
*/
void idDxtEncoder::RotateNormalsDXT5( byte* block ) const
{
2012-11-26 18:58:24 +00:00
byte rotatedBlock[64];
byte col1[4];
byte col2[4];
int bestError = MAX_TYPE( int );
int bestRotation = 0;
int bestScale = 1;
for( int i = 0; i < 32; i += 1 )
{
2012-11-26 18:58:24 +00:00
int r = ( i << 3 ) | ( i >> 2 );
float angle = ( r / 255.0f ) * idMath::PI;
float s = sin( angle );
float c = cos( angle );
for( int j = 0; j < 16; j++ )
{
float x = block[j * 4 + 3] / 255.0f * 2.0f - 1.0f;
float y = block[j * 4 + 1] / 255.0f * 2.0f - 1.0f;
2012-11-26 18:58:24 +00:00
float rx = c * x - s * y;
float ry = s * x + c * y;
rotatedBlock[j * 4 + 3] = idMath::Ftob( ( rx + 1.0f ) / 2.0f * 255.0f );
rotatedBlock[j * 4 + 1] = idMath::Ftob( ( ry + 1.0f ) / 2.0f * 255.0f );
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
#if USE_SCALE
byte minColor = 255;
byte maxColor = 0;
for( int j = 0; j < 16; j++ )
{
if( rotatedBlock[j * 4 + 1] < minColor )
{
minColor = rotatedBlock[j * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( rotatedBlock[j * 4 + 1] > maxColor )
{
maxColor = rotatedBlock[j * 4 + 1];
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
const int s0 = 128 / 2 - 1;
const int s1 = 128 / 4 - 1;
2012-11-26 18:58:24 +00:00
int range = Max( abs( minColor - 128 ), abs( maxColor - 128 ) );
int scale = 1 + ( range <= s0 ) + 2 * ( range <= s1 );
for( int j = 0; j < 16; j++ )
{
rotatedBlock[j * 4 + 1] = byte( ( rotatedBlock[j * 4 + 1] - 128 ) * scale + 128 );
2012-11-26 18:58:24 +00:00
}
#endif
2012-11-26 18:58:24 +00:00
int errorY = GetMinMaxNormalYHQ( rotatedBlock, col1, col2, true, scale );
int errorX = GetMinMaxAlphaHQ( rotatedBlock, 3, col1, col2 );
int error = errorX + errorY;
if( error < bestError )
{
2012-11-26 18:58:24 +00:00
bestError = error;
bestRotation = r;
bestScale = scale;
}
}
2012-11-26 18:58:24 +00:00
float angle = ( bestRotation / 255.0f ) * idMath::PI;
float s = sin( angle );
float c = cos( angle );
for( int j = 0; j < 16; j++ )
{
float x = block[j * 4 + 3] / 255.0f * 2.0f - 1.0f;
float y = block[j * 4 + 1] / 255.0f * 2.0f - 1.0f;
2012-11-26 18:58:24 +00:00
float rx = c * x - s * y;
float ry = s * x + c * y;
block[j * 4 + 0] = ( byte )bestRotation;
block[j * 4 + 1] = idMath::Ftob( ( ry + 1.0f ) / 2.0f * 255.0f );
block[j * 4 + 3] = idMath::Ftob( ( rx + 1.0f ) / 2.0f * 255.0f );
2012-11-26 18:58:24 +00:00
#if USE_SCALE
block[j * 4 + 1] = byte( ( block[j * 4 + 1] - 128 ) * bestScale + 128 );
block[j * 4 + 2] = byte( ( bestScale - 1 ) << 3 );
2012-11-26 18:58:24 +00:00
#endif
}
}
/*
========================
idDxtEncoder::CompressNormalMapDXT5HQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressNormalMapDXT5HQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
byte alphaIndices1[6];
byte alphaIndices2[6];
unsigned int colorIndices;
byte col1[4];
byte col2[4];
int error1;
int error2;
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorDXT5( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
// swizzle components
for( int k = 0; k < 16; k++ )
{
block[k * 4 + 3] = block[k * 4 + 0];
block[k * 4 + 0] = 0;
block[k * 4 + 2] = 0;
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
//BiasScaleNormalY( block );
//RotateNormalsDXT5( block );
2012-11-26 18:58:24 +00:00
GetMinMaxNormalYHQ( block, col1, col2, true, 1 );
GetMinMaxAlphaHQ( block, 3, col1, col2 );
2012-11-26 18:58:24 +00:00
// Write out alpha data. Try and find minimum error for the two encoding methods.
error1 = FindAlphaIndices( block, 3, col1[3], col2[3], alphaIndices1 );
error2 = FindAlphaIndices( block, 3, col2[3], col1[3], alphaIndices2 );
if( error1 < error2 )
{
2012-11-26 18:58:24 +00:00
EmitByte( col1[3] );
EmitByte( col2[3] );
EmitByte( alphaIndices1[0] );
EmitByte( alphaIndices1[1] );
EmitByte( alphaIndices1[2] );
EmitByte( alphaIndices1[3] );
EmitByte( alphaIndices1[4] );
EmitByte( alphaIndices1[5] );
}
else
{
2012-11-26 18:58:24 +00:00
EmitByte( col2[3] );
EmitByte( col1[3] );
EmitByte( alphaIndices2[0] );
EmitByte( alphaIndices2[1] );
EmitByte( alphaIndices2[2] );
EmitByte( alphaIndices2[3] );
EmitByte( alphaIndices2[4] );
EmitByte( alphaIndices2[5] );
}
2012-11-26 18:58:24 +00:00
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
NV4XHardwareBugFix( col2, col1 );
#endif
2012-11-26 18:58:24 +00:00
// Write out color data. Always take the path with 4 interpolated values.
unsigned short scol1 = ColorTo565( col1 );
unsigned short scol2 = ColorTo565( col2 );
2012-11-26 18:58:24 +00:00
EmitUShort( scol1 );
EmitUShort( scol2 );
2012-11-26 18:58:24 +00:00
FindColorIndices( block, scol1, scol2, colorIndices );
EmitUInt( colorIndices );
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r100%%\n" );
}
/*
========================
idDxtEncoder::CompressNormalMapDXT5RenormalizeHQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressNormalMapDXT5RenormalizeHQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
unsigned int colorIndices;
byte alphaIndices[6];
byte col1[4];
byte col2[4];
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorDXT5( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
// swizzle components
for( int k = 0; k < 16; k++ )
{
2012-11-26 18:58:24 +00:00
#if 0 // object-space
block[k * 4 + 3] = block[k * 4 + 2];
block[k * 4 + 2] = 0;
2012-11-26 18:58:24 +00:00
#else
block[k * 4 + 3] = block[k * 4 + 0];
block[k * 4 + 0] = 0;
2012-11-26 18:58:24 +00:00
#endif
}
2012-11-26 18:58:24 +00:00
GetMinMaxNormalsDXT5HQFast( block, col1, col2, colorIndices, alphaIndices );
2012-11-26 18:58:24 +00:00
EmitByte( col2[3] );
EmitByte( col1[3] );
EmitByte( alphaIndices[0] );
EmitByte( alphaIndices[1] );
EmitByte( alphaIndices[2] );
EmitByte( alphaIndices[3] );
EmitByte( alphaIndices[4] );
EmitByte( alphaIndices[5] );
2012-11-26 18:58:24 +00:00
unsigned short scol1 = ColorTo565( col1 );
unsigned short scol2 = ColorTo565( col2 );
2012-11-26 18:58:24 +00:00
EmitUShort( scol2 );
EmitUShort( scol1 );
EmitUInt( colorIndices );
2012-11-26 18:58:24 +00:00
////idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
////idLib::Printf( "\r100%%\n" );
}
/*
========================
idDxtEncoder::CompressNormalMapDXN2HQ
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressNormalMapDXN2HQ( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
byte alphaIndices1[6];
byte alphaIndices2[6];
byte col1[4];
byte col2[4];
int error1;
int error2;
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
WriteTinyColorDXT5( inBuf, width, height );
return;
}
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
for( int k = 0; k < 2; k++ )
{
2012-11-26 18:58:24 +00:00
GetMinMaxAlphaHQ( block, k, col1, col2 );
2012-11-26 18:58:24 +00:00
// Write out alpha data. Try and find minimum error for the two encoding methods.
error1 = FindAlphaIndices( block, k, col1[k], col2[k], alphaIndices1 );
error2 = FindAlphaIndices( block, k, col2[k], col1[k], alphaIndices2 );
if( error1 < error2 )
{
2012-11-26 18:58:24 +00:00
EmitByte( col1[k] );
EmitByte( col2[k] );
EmitByte( alphaIndices1[0] );
EmitByte( alphaIndices1[1] );
EmitByte( alphaIndices1[2] );
EmitByte( alphaIndices1[3] );
EmitByte( alphaIndices1[4] );
EmitByte( alphaIndices1[5] );
}
else
{
2012-11-26 18:58:24 +00:00
EmitByte( col2[k] );
EmitByte( col1[k] );
EmitByte( alphaIndices2[0] );
EmitByte( alphaIndices2[1] );
EmitByte( alphaIndices2[2] );
EmitByte( alphaIndices2[3] );
EmitByte( alphaIndices2[4] );
EmitByte( alphaIndices2[5] );
}
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
}
outData += dstPadding;
inBuf += srcPadding;
}
2012-11-26 18:58:24 +00:00
//idLib::Printf( "\r100%%\n" );
}
/*
========================
idDxtEncoder::GetMinMaxBBox
Takes the extents of the bounding box of the colors in the 4x4 block in RGB space.
Also finds the minimum and maximum alpha values.
params: colorBlock - 4*4 input tile, 4 bytes per pixel
paramO: minColor - 4 byte Min color found
paramO: maxColor - 4 byte Max color found
========================
*/
ID_INLINE void idDxtEncoder::GetMinMaxBBox( const byte* colorBlock, byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
minColor[0] = minColor[1] = minColor[2] = minColor[3] = 255;
maxColor[0] = maxColor[1] = maxColor[2] = maxColor[3] = 0;
for( int i = 0; i < 16; i++ )
{
if( colorBlock[i * 4 + 0] < minColor[0] )
{
minColor[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] < minColor[1] )
{
minColor[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] < minColor[2] )
{
minColor[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 3] < minColor[3] )
{
minColor[3] = colorBlock[i * 4 + 3];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 0] > maxColor[0] )
{
maxColor[0] = colorBlock[i * 4 + 0];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 1] > maxColor[1] )
{
maxColor[1] = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 2] > maxColor[2] )
{
maxColor[2] = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
}
if( colorBlock[i * 4 + 3] > maxColor[3] )
{
maxColor[3] = colorBlock[i * 4 + 3];
2012-11-26 18:58:24 +00:00
}
}
}
/*
========================
idDxtEncoder::InsetColorsBBox
========================
*/
ID_INLINE void idDxtEncoder::InsetColorsBBox( byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
byte inset[4];
2012-11-26 18:58:24 +00:00
inset[0] = ( maxColor[0] - minColor[0] ) >> INSET_COLOR_SHIFT;
inset[1] = ( maxColor[1] - minColor[1] ) >> INSET_COLOR_SHIFT;
inset[2] = ( maxColor[2] - minColor[2] ) >> INSET_COLOR_SHIFT;
inset[3] = ( maxColor[3] - minColor[3] ) >> INSET_ALPHA_SHIFT;
2012-11-26 18:58:24 +00:00
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;
2012-11-26 18:58:24 +00:00
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;
}
/*
========================
idDxtEncoder::SelectColorsDiagonal
========================
*/
void idDxtEncoder::SelectColorsDiagonal( const byte* colorBlock, byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
byte mid0 = byte( ( ( int ) minColor[0] + maxColor[0] + 1 ) >> 1 );
byte mid1 = byte( ( ( int ) minColor[1] + maxColor[1] + 1 ) >> 1 );
byte mid2 = byte( ( ( int ) minColor[2] + maxColor[2] + 1 ) >> 1 );
2012-11-26 18:58:24 +00:00
#if 0
2012-11-26 18:58:24 +00:00
// using the covariance is the best way to select the diagonal
int side0 = 0;
int side1 = 0;
for( int i = 0; i < 16; i++ )
{
int b0 = colorBlock[i * 4 + 0] - mid0;
int b1 = colorBlock[i * 4 + 1] - mid1;
int b2 = colorBlock[i * 4 + 2] - mid2;
2012-11-26 18:58:24 +00:00
side0 += ( b0 * b1 );
side1 += ( b1 * b2 );
}
byte mask0 = -( side0 < 0 );
byte mask1 = -( side1 < 0 );
2012-11-26 18:58:24 +00:00
#else
2012-11-26 18:58:24 +00:00
// calculating the covariance of just the sign bits is much faster and gives almost the same result
int side0 = 0;
int side1 = 0;
for( int i = 0; i < 16; i++ )
{
byte b0 = colorBlock[i * 4 + 0] >= mid0;
byte b1 = colorBlock[i * 4 + 1] >= mid1;
byte b2 = colorBlock[i * 4 + 2] >= mid2;
2012-11-26 18:58:24 +00:00
side0 += ( b0 ^ b1 );
side1 += ( b1 ^ b2 );
}
byte mask0 = -( side0 > 8 );
byte mask1 = -( side1 > 8 );
2012-11-26 18:58:24 +00:00
#endif
2012-11-26 18:58:24 +00:00
byte c0 = minColor[0];
byte c1 = maxColor[0];
byte c2 = minColor[2];
byte c3 = maxColor[2];
2012-11-26 18:58:24 +00:00
c0 ^= c1;
mask0 &= c0;
c1 ^= mask0;
c0 ^= c1;
2012-11-26 18:58:24 +00:00
c2 ^= c3;
mask1 &= c2;
c3 ^= mask1;
c2 ^= c3;
2012-11-26 18:58:24 +00:00
minColor[0] = c0;
maxColor[0] = c1;
minColor[2] = c2;
maxColor[2] = c3;
if( ColorTo565( minColor ) > ColorTo565( maxColor ) )
{
2012-11-26 18:58:24 +00:00
SwapValues( minColor[0], maxColor[0] );
SwapValues( minColor[1], maxColor[1] );
SwapValues( minColor[2], maxColor[2] );
}
}
/*
========================
idDxtEncoder::EmitColorIndices
params: colorBlock - 16 pixel block for which find color indexes
paramO: minColor - Min color found
paramO: maxColor - Max color found
return: 4 byte color index block
========================
*/
void idDxtEncoder::EmitColorIndices( const byte* colorBlock, const byte* minColor, const byte* maxColor )
{
2012-11-26 18:58:24 +00:00
#if 1
ALIGN16( uint16 colors[4][4] );
unsigned int result = 0;
2012-11-26 18:58:24 +00:00
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[0][3] = 0;
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[1][3] = 0;
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[2][3] = 0;
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;
colors[3][3] = 0;
2012-11-26 18:58:24 +00:00
// uses sum of absolute differences instead of squared distance to find the best match
for( int i = 15; i >= 0; i-- )
{
2012-11-26 18:58:24 +00:00
int c0, c1, c2, c3, m, d0, d1, d2, d3;
c0 = colorBlock[i * 4 + 0];
c1 = colorBlock[i * 4 + 1];
c2 = colorBlock[i * 4 + 2];
c3 = colorBlock[i * 4 + 3];
2012-11-26 18:58:24 +00:00
m = colors[0][0] - c0;
d0 = abs( m );
m = colors[1][0] - c0;
d1 = abs( m );
m = colors[2][0] - c0;
d2 = abs( m );
m = colors[3][0] - c0;
d3 = abs( m );
2012-11-26 18:58:24 +00:00
m = colors[0][1] - c1;
d0 += abs( m );
m = colors[1][1] - c1;
d1 += abs( m );
m = colors[2][1] - c1;
d2 += abs( m );
m = colors[3][1] - c1;
d3 += abs( m );
2012-11-26 18:58:24 +00:00
m = colors[0][2] - c2;
d0 += abs( m );
m = colors[1][2] - c2;
d1 += abs( m );
m = colors[2][2] - c2;
d2 += abs( m );
m = colors[3][2] - c2;
d3 += abs( m );
2012-11-26 18:58:24 +00:00
#if 0
int b0 = d0 > d2;
int b1 = d1 > d3;
int b2 = d0 > d3;
int b3 = d1 > d2;
int b4 = d0 > d1;
int b5 = d2 > d3;
2012-11-26 18:58:24 +00:00
result |= ( ( !b3 & b4 ) | ( b2 & b5 ) | ( ( ( b0 & b3 ) | ( b1 & b2 ) ) << 1 ) ) << ( i << 1 );
#else
bool b0 = d0 > d3;
bool b1 = d1 > d2;
bool b2 = d0 > d2;
bool b3 = d1 > d3;
bool b4 = d2 > d3;
2012-11-26 18:58:24 +00:00
int x0 = b1 & b2;
int x1 = b0 & b3;
int x2 = b0 & b4;
2012-11-26 18:58:24 +00:00
result |= ( x2 | ( ( x0 | x1 ) << 1 ) ) << ( i << 1 );
#endif
}
2012-11-26 18:58:24 +00:00
EmitUInt( result );
2012-11-26 18:58:24 +00:00
#elif 1
2012-11-26 18:58:24 +00:00
byte colors[4][4];
unsigned int indexes[16];
2012-11-26 18:58:24 +00:00
colors[0][0] = ( maxColor[0] & C565_5_MASK ) | ( maxColor[0] >> 6 );
colors[0][1] = ( maxColor[1] & C565_6_MASK ) | ( maxColor[1] >> 5 );
colors[0][2] = ( maxColor[2] & C565_5_MASK ) | ( maxColor[2] >> 6 );
colors[0][3] = 0;
colors[1][0] = ( minColor[0] & C565_5_MASK ) | ( minColor[0] >> 6 );
colors[1][1] = ( minColor[1] & C565_6_MASK ) | ( minColor[1] >> 5 );
colors[1][2] = ( minColor[2] & C565_5_MASK ) | ( minColor[2] >> 6 );
colors[1][3] = 0;
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[2][3] = 0;
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;
colors[3][3] = 0;
for( int i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
int c0, c1, c2, m, d, minDist;
c0 = colorBlock[i * 4 + 0];
c1 = colorBlock[i * 4 + 1];
c2 = colorBlock[i * 4 + 2];
2012-11-26 18:58:24 +00:00
m = colors[0][0] - c0;
d = m * m;
m = colors[0][1] - c1;
d += m * m;
m = colors[0][2] - c2;
d += m * m;
2012-11-26 18:58:24 +00:00
minDist = d;
indexes[i] = 0;
2012-11-26 18:58:24 +00:00
m = colors[1][0] - c0;
d = m * m;
m = colors[1][1] - c1;
d += m * m;
m = colors[1][2] - c2;
d += m * m;
if( d < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = d;
indexes[i] = 1;
}
2012-11-26 18:58:24 +00:00
m = colors[2][0] - c0;
d = m * m;
m = colors[2][1] - c1;
d += m * m;
m = colors[2][2] - c2;
d += m * m;
if( d < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = d;
indexes[i] = 2;
}
2012-11-26 18:58:24 +00:00
m = colors[3][0] - c0;
d = m * m;
m = colors[3][1] - c1;
d += m * m;
m = colors[3][2] - c2;
d += m * m;
if( d < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = d;
indexes[i] = 3;
}
}
2012-11-26 18:58:24 +00:00
unsigned int result = 0;
for( int i = 0; i < 16; i++ )
{
result |= ( indexes[i] << ( unsigned int )( i << 1 ) );
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
EmitUInt( result );
2012-11-26 18:58:24 +00:00
#else
2012-11-26 18:58:24 +00:00
byte colors[4][4];
unsigned int indexes[16];
2012-11-26 18:58:24 +00:00
colors[0][0] = ( maxColor[0] & C565_5_MASK ) | ( maxColor[0] >> 6 );
colors[0][1] = ( maxColor[1] & C565_6_MASK ) | ( maxColor[1] >> 5 );
colors[0][2] = ( maxColor[2] & C565_5_MASK ) | ( maxColor[2] >> 6 );
colors[0][3] = 0;
colors[1][0] = ( minColor[0] & C565_5_MASK ) | ( minColor[0] >> 6 );
colors[1][1] = ( minColor[1] & C565_6_MASK ) | ( minColor[1] >> 5 );
colors[1][2] = ( minColor[2] & C565_5_MASK ) | ( minColor[2] >> 6 );
colors[1][3] = 0;
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[2][3] = 0;
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;
colors[3][3] = 0;
for( int i = 0; i < 16; i++ )
{
unsigned int minDist = ( 255 * 255 ) * 4;
for( int j = 0; j < 4; j++ )
{
unsigned int dist = ColorDistance( &colorBlock[i * 4], &colors[j][0] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
indexes[i] = j;
}
}
}
2012-11-26 18:58:24 +00:00
unsigned int result = 0;
for( int i = 0; i < 16; i++ )
{
result |= ( indexes[i] << ( unsigned int )( i << 1 ) );
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
EmitUInt( result );
2012-11-26 18:58:24 +00:00
#endif
}
/*
========================
idDxtEncoder::EmitColorAlphaIndices
params: colorBlock - 16 pixel block for which find color indexes
paramO: minColor - Min color found
paramO: maxColor - Max color found
return: 4 byte color index block
========================
*/
void idDxtEncoder::EmitColorAlphaIndices( const byte* colorBlock, const byte* minColor, const byte* maxColor )
{
2012-11-26 18:58:24 +00:00
ALIGN16( uint16 colors[4][4] );
unsigned int result = 0;
2012-11-26 18:58:24 +00:00
colors[0][0] = ( minColor[0] & C565_5_MASK ) | ( minColor[0] >> 5 );
colors[0][1] = ( minColor[1] & C565_6_MASK ) | ( minColor[1] >> 6 );
colors[0][2] = ( minColor[2] & C565_5_MASK ) | ( minColor[2] >> 5 );
colors[0][3] = 255;
colors[1][0] = ( maxColor[0] & C565_5_MASK ) | ( maxColor[0] >> 5 );
colors[1][1] = ( maxColor[1] & C565_6_MASK ) | ( maxColor[1] >> 6 );
colors[1][2] = ( maxColor[2] & C565_5_MASK ) | ( maxColor[2] >> 5 );
colors[1][3] = 255;
colors[2][0] = ( colors[0][0] + colors[1][0] ) / 2;
colors[2][1] = ( colors[0][1] + colors[1][1] ) / 2;
colors[2][2] = ( colors[0][2] + colors[1][2] ) / 2;
colors[2][3] = 255;
colors[3][0] = 0;
colors[3][1] = 0;
colors[3][2] = 0;
colors[3][3] = 0;
2012-11-26 18:58:24 +00:00
// uses sum of absolute differences instead of squared distance to find the best match
for( int i = 15; i >= 0; i-- )
{
2012-11-26 18:58:24 +00:00
int c0, c1, c2, c3, m, d0, d1, d2;
c0 = colorBlock[i * 4 + 0];
c1 = colorBlock[i * 4 + 1];
c2 = colorBlock[i * 4 + 2];
c3 = colorBlock[i * 4 + 3];
2012-11-26 18:58:24 +00:00
m = colors[0][0] - c0;
d0 = abs( m );
m = colors[1][0] - c0;
d1 = abs( m );
m = colors[2][0] - c0;
d2 = abs( m );
2012-11-26 18:58:24 +00:00
m = colors[0][1] - c1;
d0 += abs( m );
m = colors[1][1] - c1;
d1 += abs( m );
m = colors[2][1] - c1;
d2 += abs( m );
2012-11-26 18:58:24 +00:00
m = colors[0][2] - c2;
d0 += abs( m );
m = colors[1][2] - c2;
d1 += abs( m );
m = colors[2][2] - c2;
d2 += abs( m );
2012-11-26 18:58:24 +00:00
unsigned int b0 = d2 > d0;
unsigned int b1 = d2 > d1;
unsigned int b2 = d1 > d0;
unsigned int b3 = c3 < 128;
2012-12-22 05:15:50 +00:00
// DG: add some parenthesis to appease (often rightly) warning compiler
result |= ( ( ( ( b0 & b1 ) | b3 ) << 1 ) | ( ( b2 ^ b1 ) | b3 ) ) << ( i << 1 );
// DG end
2012-11-26 18:58:24 +00:00
}
2012-11-26 18:58:24 +00:00
EmitUInt( result );
}
/*
========================
idDxtEncoder::EmitCTX1Indices
params: colorBlock - 16 pixel block for which find color indexes
paramO: minColor - Min color found
paramO: maxColor - Max color found
return: 4 byte color index block
========================
*/
void idDxtEncoder::EmitCTX1Indices( const byte* colorBlock, const byte* minColor, const byte* maxColor )
{
2012-11-26 18:58:24 +00:00
ALIGN16( uint16 colors[4][2] );
unsigned int result = 0;
2012-11-26 18:58:24 +00:00
colors[0][0] = maxColor[0];
colors[0][1] = maxColor[1];
colors[1][0] = minColor[0];
colors[1][1] = minColor[1];
2012-11-26 18:58:24 +00:00
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[3][0] = ( 1 * colors[0][0] + 2 * colors[1][0] ) / 3;
colors[3][1] = ( 1 * colors[0][1] + 2 * colors[1][1] ) / 3;
for( int i = 15; i >= 0; i-- )
{
2012-11-26 18:58:24 +00:00
int c0, c1, m, d0, d1, d2, d3;
c0 = colorBlock[i * 4 + 0];
c1 = colorBlock[i * 4 + 1];
2012-11-26 18:58:24 +00:00
m = colors[0][0] - c0;
d0 = abs( m );
m = colors[1][0] - c0;
d1 = abs( m );
m = colors[2][0] - c0;
d2 = abs( m );
m = colors[3][0] - c0;
d3 = abs( m );
2012-11-26 18:58:24 +00:00
m = colors[0][1] - c1;
d0 += abs( m );
m = colors[1][1] - c1;
d1 += abs( m );
m = colors[2][1] - c1;
d2 += abs( m );
m = colors[3][1] - c1;
d3 += abs( m );
2012-11-26 18:58:24 +00:00
bool b0 = d0 > d3;
bool b1 = d1 > d2;
bool b2 = d0 > d2;
bool b3 = d1 > d3;
bool b4 = d2 > d3;
2012-11-26 18:58:24 +00:00
int x0 = b1 & b2;
int x1 = b0 & b3;
int x2 = b0 & b4;
2012-11-26 18:58:24 +00:00
result |= ( x2 | ( ( x0 | x1 ) << 1 ) ) << ( i << 1 );
}
2012-11-26 18:58:24 +00:00
EmitUInt( result );
}
/*
========================
idDxtEncoder::EmitAlphaIndices
params: colorBlock - 16 pixel block for which find alpha indexes
paramO: minAlpha - Min alpha found
paramO: maxAlpha - Max alpha found
========================
*/
void idDxtEncoder::EmitAlphaIndices( const byte* colorBlock, const int offset, const byte minAlpha, const byte maxAlpha )
{
2012-11-26 18:58:24 +00:00
assert( maxAlpha >= minAlpha );
2012-11-26 18:58:24 +00:00
const int ALPHA_RANGE = 7;
2012-11-26 18:58:24 +00:00
#if 1
2012-11-26 18:58:24 +00:00
byte ab1, ab2, ab3, ab4, ab5, ab6, ab7;
ALIGN16( byte indexes[16] );
ab1 = ( 13 * maxAlpha + 1 * minAlpha + ALPHA_RANGE ) / ( ALPHA_RANGE * 2 );
ab2 = ( 11 * maxAlpha + 3 * minAlpha + ALPHA_RANGE ) / ( ALPHA_RANGE * 2 );
ab3 = ( 9 * maxAlpha + 5 * minAlpha + ALPHA_RANGE ) / ( ALPHA_RANGE * 2 );
ab4 = ( 7 * maxAlpha + 7 * minAlpha + ALPHA_RANGE ) / ( ALPHA_RANGE * 2 );
ab5 = ( 5 * maxAlpha + 9 * minAlpha + ALPHA_RANGE ) / ( ALPHA_RANGE * 2 );
ab6 = ( 3 * maxAlpha + 11 * minAlpha + ALPHA_RANGE ) / ( ALPHA_RANGE * 2 );
ab7 = ( 1 * maxAlpha + 13 * minAlpha + ALPHA_RANGE ) / ( ALPHA_RANGE * 2 );
2012-11-26 18:58:24 +00:00
colorBlock += offset;
for( int i = 0; i < 16; i++ )
{
byte a = colorBlock[i * 4];
2012-11-26 18:58:24 +00:00
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 = ( 8 - b1 - b2 - b3 - b4 - b5 - b6 - b7 ) & 7;
indexes[i] = byte( index ^ ( 2 > index ) );
}
EmitByte( ( indexes[ 0] >> 0 ) | ( indexes[ 1] << 3 ) | ( indexes[ 2] << 6 ) );
EmitByte( ( indexes[ 2] >> 2 ) | ( indexes[ 3] << 1 ) | ( indexes[ 4] << 4 ) | ( indexes[ 5] << 7 ) );
EmitByte( ( indexes[ 5] >> 1 ) | ( indexes[ 6] << 2 ) | ( indexes[ 7] << 5 ) );
EmitByte( ( indexes[ 8] >> 0 ) | ( indexes[ 9] << 3 ) | ( indexes[10] << 6 ) );
EmitByte( ( indexes[10] >> 2 ) | ( indexes[11] << 1 ) | ( indexes[12] << 4 ) | ( indexes[13] << 7 ) );
EmitByte( ( indexes[13] >> 1 ) | ( indexes[14] << 2 ) | ( indexes[15] << 5 ) );
2012-11-26 18:58:24 +00:00
#elif 0
2012-11-26 18:58:24 +00:00
ALIGN16( byte indexes[16] );
byte delta = maxAlpha - minAlpha;
byte half = delta >> 1;
byte bias = delta / ( 2 * ALPHA_RANGE );
byte bottom = minAlpha + bias;
byte top = maxAlpha - bias;
2012-11-26 18:58:24 +00:00
colorBlock += offset;
for( int i = 0; i < 16; i++ )
{
byte a = colorBlock[i * 4];
if( a <= bottom )
{
2012-11-26 18:58:24 +00:00
indexes[i] = 1;
}
else if( a >= top )
{
2012-11-26 18:58:24 +00:00
indexes[i] = 0;
}
else
{
indexes[i] = ( ALPHA_RANGE + 1 ) + ( ( minAlpha - a ) * ALPHA_RANGE - half ) / delta;
2012-11-26 18:58:24 +00:00
}
}
EmitByte( ( indexes[ 0] >> 0 ) | ( indexes[ 1] << 3 ) | ( indexes[ 2] << 6 ) );
EmitByte( ( indexes[ 2] >> 2 ) | ( indexes[ 3] << 1 ) | ( indexes[ 4] << 4 ) | ( indexes[ 5] << 7 ) );
EmitByte( ( indexes[ 5] >> 1 ) | ( indexes[ 6] << 2 ) | ( indexes[ 7] << 5 ) );
EmitByte( ( indexes[ 8] >> 0 ) | ( indexes[ 9] << 3 ) | ( indexes[10] << 6 ) );
EmitByte( ( indexes[10] >> 2 ) | ( indexes[11] << 1 ) | ( indexes[12] << 4 ) | ( indexes[13] << 7 ) );
EmitByte( ( indexes[13] >> 1 ) | ( indexes[14] << 2 ) | ( indexes[15] << 5 ) );
2012-11-26 18:58:24 +00:00
#elif 0
2012-11-26 18:58:24 +00:00
ALIGN16( byte indexes[16] );
byte delta = maxAlpha - minAlpha;
byte half = delta >> 1;
byte bias = delta / ( 2 * ALPHA_RANGE );
byte bottom = minAlpha + bias;
byte top = maxAlpha - bias;
2012-11-26 18:58:24 +00:00
colorBlock += offset;
for( int i = 0; i < 16; i++ )
{
byte a = colorBlock[i * 4];
int index = ( ALPHA_RANGE + 1 ) + ( ( minAlpha - a ) * ALPHA_RANGE - half ) / delta;
2012-11-26 18:58:24 +00:00
int c0 = a > bottom;
int c1 = a < top;
indexes[i] = ( index & -( c0 & c1 ) ) | ( c0 ^ 1 );
}
EmitByte( ( indexes[ 0] >> 0 ) | ( indexes[ 1] << 3 ) | ( indexes[ 2] << 6 ) );
EmitByte( ( indexes[ 2] >> 2 ) | ( indexes[ 3] << 1 ) | ( indexes[ 4] << 4 ) | ( indexes[ 5] << 7 ) );
EmitByte( ( indexes[ 5] >> 1 ) | ( indexes[ 6] << 2 ) | ( indexes[ 7] << 5 ) );
EmitByte( ( indexes[ 8] >> 0 ) | ( indexes[ 9] << 3 ) | ( indexes[10] << 6 ) );
EmitByte( ( indexes[10] >> 2 ) | ( indexes[11] << 1 ) | ( indexes[12] << 4 ) | ( indexes[13] << 7 ) );
EmitByte( ( indexes[13] >> 1 ) | ( indexes[14] << 2 ) | ( indexes[15] << 5 ) );
2012-11-26 18:58:24 +00:00
#else
2012-11-26 18:58:24 +00:00
ALIGN16( byte indexes[16] );
ALIGN16( byte alphas[8] );
2012-11-26 18:58:24 +00:00
alphas[0] = maxAlpha;
alphas[1] = minAlpha;
alphas[2] = ( 6 * maxAlpha + 1 * minAlpha ) / ALPHA_RANGE;
alphas[3] = ( 5 * maxAlpha + 2 * minAlpha ) / ALPHA_RANGE;
alphas[4] = ( 4 * maxAlpha + 3 * minAlpha ) / ALPHA_RANGE;
alphas[5] = ( 3 * maxAlpha + 4 * minAlpha ) / ALPHA_RANGE;
alphas[6] = ( 2 * maxAlpha + 5 * minAlpha ) / ALPHA_RANGE;
alphas[7] = ( 1 * maxAlpha + 6 * minAlpha ) / ALPHA_RANGE;
2012-11-26 18:58:24 +00:00
colorBlock += offset;
for( int i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
int minDist = INT_MAX;
byte a = colorBlock[i * 4];
for( int j = 0; j < 8; j++ )
{
2012-11-26 18:58:24 +00:00
int dist = abs( a - alphas[j] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
indexes[i] = j;
}
}
}
EmitByte( ( indexes[ 0] >> 0 ) | ( indexes[ 1] << 3 ) | ( indexes[ 2] << 6 ) );
EmitByte( ( indexes[ 2] >> 2 ) | ( indexes[ 3] << 1 ) | ( indexes[ 4] << 4 ) | ( indexes[ 5] << 7 ) );
EmitByte( ( indexes[ 5] >> 1 ) | ( indexes[ 6] << 2 ) | ( indexes[ 7] << 5 ) );
EmitByte( ( indexes[ 8] >> 0 ) | ( indexes[ 9] << 3 ) | ( indexes[10] << 6 ) );
EmitByte( ( indexes[10] >> 2 ) | ( indexes[11] << 1 ) | ( indexes[12] << 4 ) | ( indexes[13] << 7 ) );
EmitByte( ( indexes[13] >> 1 ) | ( indexes[14] << 2 ) | ( indexes[15] << 5 ) );
2012-11-26 18:58:24 +00:00
#endif
}
/*
========================
idDxtEncoder::CompressImageDXT1Fast_Generic
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressImageDXT1Fast_Generic( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
ALIGN16( byte minColor[4] );
ALIGN16( byte maxColor[4] );
2012-11-26 18:58:24 +00:00
assert( width >= 4 && ( width & 3 ) == 0 );
assert( height >= 4 && ( height & 3 ) == 0 );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxBBox( block, minColor, maxColor );
//SelectColorsDiagonal( block, minColor, maxColor );
InsetColorsBBox( minColor, maxColor );
2012-11-26 18:58:24 +00:00
EmitUShort( ColorTo565( maxColor ) );
EmitUShort( ColorTo565( minColor ) );
2012-11-26 18:58:24 +00:00
EmitColorIndices( block, minColor, maxColor );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::CompressImageDXT1AlphaFast_Generic
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressImageDXT1AlphaFast_Generic( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
ALIGN16( byte minColor[4] );
ALIGN16( byte maxColor[4] );
2012-11-26 18:58:24 +00:00
assert( width >= 4 && ( width & 3 ) == 0 );
assert( height >= 4 && ( height & 3 ) == 0 );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxBBox( block, minColor, maxColor );
byte minAlpha = minColor[3];
//SelectColorsDiagonal( block, minColor, maxColor );
InsetColorsBBox( minColor, maxColor );
if( minAlpha >= 128 )
{
2012-11-26 18:58:24 +00:00
EmitUShort( ColorTo565( maxColor ) );
EmitUShort( ColorTo565( minColor ) );
EmitColorIndices( block, minColor, maxColor );
}
else
{
2012-11-26 18:58:24 +00:00
EmitUShort( ColorTo565( minColor ) );
EmitUShort( ColorTo565( maxColor ) );
EmitColorAlphaIndices( block, minColor, maxColor );
}
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::CompressImageDXT5Fast_Generic
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressImageDXT5Fast_Generic( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
ALIGN16( byte minColor[4] );
ALIGN16( byte maxColor[4] );
2012-11-26 18:58:24 +00:00
assert( width >= 4 && ( width & 3 ) == 0 );
assert( height >= 4 && ( height & 3 ) == 0 );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxBBox( block, minColor, maxColor );
//SelectColorsDiagonal( block, minColor, maxColor );
InsetColorsBBox( minColor, maxColor );
2012-11-26 18:58:24 +00:00
EmitByte( maxColor[3] );
EmitByte( minColor[3] );
2012-11-26 18:58:24 +00:00
EmitAlphaIndices( block, 3, minColor[3], maxColor[3] );
2012-11-26 18:58:24 +00:00
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
// the colors are already always guaranteed to be sorted properly
#endif
2012-11-26 18:58:24 +00:00
EmitUShort( ColorTo565( maxColor ) );
EmitUShort( ColorTo565( minColor ) );
2012-11-26 18:58:24 +00:00
EmitColorIndices( block, minColor, maxColor );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::ScaleYCoCg
========================
*/
void idDxtEncoder::ScaleYCoCg( byte* colorBlock, byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
int m0 = abs( minColor[0] - 128 );
int m1 = abs( minColor[1] - 128 );
int m2 = abs( maxColor[0] - 128 );
int m3 = abs( maxColor[1] - 128 );
if( m1 > m0 ) m0 = m1;
if( m3 > m2 ) m2 = m3;
if( m2 > m0 ) m0 = m2;
2012-11-26 18:58:24 +00:00
const int s0 = 128 / 2 - 1;
const int s1 = 128 / 4 - 1;
2012-11-26 18:58:24 +00:00
int mask0 = -( m0 <= s0 );
int mask1 = -( m0 <= s1 );
int scale = 1 + ( 1 & mask0 ) + ( 2 & mask1 );
2012-11-26 18:58:24 +00:00
minColor[0] = byte( ( minColor[0] - 128 ) * scale + 128 );
minColor[1] = byte( ( minColor[1] - 128 ) * scale + 128 );
minColor[2] = byte( ( scale - 1 ) << 3 );
maxColor[0] = byte( ( maxColor[0] - 128 ) * scale + 128 );
maxColor[1] = byte( ( maxColor[1] - 128 ) * scale + 128 );
maxColor[2] = byte( ( scale - 1 ) << 3 );
for( int i = 0; i < 16; i++ )
{
colorBlock[i * 4 + 0] = byte( ( colorBlock[i * 4 + 0] - 128 ) * scale + 128 );
colorBlock[i * 4 + 1] = byte( ( colorBlock[i * 4 + 1] - 128 ) * scale + 128 );
2012-11-26 18:58:24 +00:00
}
}
/*
========================
idDxtEncoder::InsetYCoCgBBox
========================
*/
ID_INLINE void idDxtEncoder::InsetYCoCgBBox( byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
#if 0
byte inset[4];
2012-11-26 18:58:24 +00:00
inset[0] = ( maxColor[0] - minColor[0] ) >> INSET_COLOR_SHIFT;
inset[1] = ( maxColor[1] - minColor[1] ) >> INSET_COLOR_SHIFT;
inset[3] = ( maxColor[3] - minColor[3] ) >> INSET_ALPHA_SHIFT;
2012-11-26 18:58:24 +00:00
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[3] = ( minColor[3] + inset[3] <= 255 ) ? minColor[3] + inset[3] : 255;
2012-11-26 18:58:24 +00:00
maxColor[0] = ( maxColor[0] >= inset[0] ) ? maxColor[0] - inset[0] : 0;
maxColor[1] = ( maxColor[1] >= inset[1] ) ? maxColor[1] - inset[1] : 0;
maxColor[3] = ( maxColor[3] >= inset[3] ) ? maxColor[3] - inset[3] : 0;
2012-11-26 18:58:24 +00:00
minColor[0] = ( minColor[0] & C565_5_MASK ) | ( minColor[0] >> 5 );
minColor[1] = ( minColor[1] & C565_6_MASK ) | ( minColor[1] >> 6 );
2012-11-26 18:58:24 +00:00
maxColor[0] = ( maxColor[0] & C565_5_MASK ) | ( maxColor[0] >> 5 );
maxColor[1] = ( maxColor[1] & C565_6_MASK ) | ( maxColor[1] >> 6 );
2012-11-26 18:58:24 +00:00
#elif 0
2012-11-26 18:58:24 +00:00
float inset[4];
float minf[4];
float maxf[4];
for( int i = 0; i < 4; i++ )
{
2012-11-26 18:58:24 +00:00
minf[i] = minColor[i] / 255.0f;
maxf[i] = maxColor[i] / 255.0f;
}
2012-11-26 18:58:24 +00:00
inset[0] = ( maxf[0] - minf[0] ) / 16.0f;
inset[1] = ( maxf[1] - minf[1] ) / 16.0f;
inset[2] = ( maxf[2] - minf[2] ) / 16.0f;
inset[3] = ( maxf[3] - minf[3] ) / 32.0f;
for( int i = 0; i < 4; i++ )
{
2012-11-26 18:58:24 +00:00
minf[i] = ( minf[i] + inset[i] <= 1.0f ) ? minf[i] + inset[i] : 1.0f;
maxf[i] = ( maxf[i] >= inset[i] ) ? maxf[i] - inset[i] : 0;
}
minColor[0] = ( ( int )floor( minf[0] * 31 ) ) & ( ( 1 << 5 ) - 1 );
minColor[1] = ( ( int )floor( minf[1] * 63 ) ) & ( ( 1 << 6 ) - 1 );
maxColor[0] = ( ( int )ceil( maxf[0] * 31 ) ) & ( ( 1 << 5 ) - 1 );
maxColor[1] = ( ( int )ceil( maxf[1] * 63 ) ) & ( ( 1 << 6 ) - 1 );
2012-11-26 18:58:24 +00:00
minColor[0] = ( minColor[0] << 3 ) | ( minColor[0] >> 2 );
minColor[1] = ( minColor[1] << 2 ) | ( minColor[1] >> 4 );
2012-11-26 18:58:24 +00:00
maxColor[0] = ( maxColor[0] << 3 ) | ( maxColor[0] >> 2 );
maxColor[1] = ( maxColor[1] << 2 ) | ( maxColor[1] >> 4 );
minColor[3] = ( int )floor( minf[3] * 255.0f );
maxColor[3] = ( int )ceil( maxf[3] * 255.0f );
2012-11-26 18:58:24 +00:00
#elif 0
2012-11-26 18:58:24 +00:00
int inset[4];
int mini[4];
int maxi[4];
2012-11-26 18:58:24 +00:00
inset[0] = ( maxColor[0] - minColor[0] );
inset[1] = ( maxColor[1] - minColor[1] );
inset[3] = ( maxColor[3] - minColor[3] );
2012-11-26 18:58:24 +00:00
mini[0] = ( minColor[0] << INSET_COLOR_SHIFT ) + inset[0];
mini[1] = ( minColor[1] << INSET_COLOR_SHIFT ) + inset[1];
mini[3] = ( minColor[3] << INSET_ALPHA_SHIFT ) + inset[3];
2012-11-26 18:58:24 +00:00
maxi[0] = ( maxColor[0] << INSET_COLOR_SHIFT ) - inset[0];
maxi[1] = ( maxColor[1] << INSET_COLOR_SHIFT ) - inset[1];
maxi[3] = ( maxColor[3] << INSET_ALPHA_SHIFT ) - inset[3];
mini[0] = ( mini[0] - ( ( 1 << ( 3 ) ) - 1 ) ) >> ( INSET_COLOR_SHIFT + 3 );
mini[1] = ( mini[1] - ( ( 1 << ( 3 ) ) - 1 ) ) >> ( INSET_COLOR_SHIFT + 2 );
mini[3] = ( mini[3] - ( ( 1 << ( 2 ) ) - 1 ) ) >> ( INSET_ALPHA_SHIFT + 0 );
maxi[0] = ( maxi[0] + ( ( 1 << ( 3 ) ) - 1 ) ) >> ( INSET_COLOR_SHIFT + 3 );
maxi[1] = ( maxi[1] + ( ( 1 << ( 3 ) ) - 1 ) ) >> ( INSET_COLOR_SHIFT + 2 );
maxi[3] = ( maxi[3] + ( ( 1 << ( 2 ) ) - 1 ) ) >> ( INSET_ALPHA_SHIFT + 0 );
if( mini[0] < 0 ) mini[0] = 0;
if( mini[1] < 0 ) mini[1] = 0;
if( mini[3] < 0 ) mini[3] = 0;
if( maxi[0] > 31 ) maxi[0] = 31;
if( maxi[1] > 63 ) maxi[1] = 63;
if( maxi[3] > 255 ) maxi[3] = 255;
2012-11-26 18:58:24 +00:00
minColor[0] = ( mini[0] << 3 ) | ( mini[0] >> 2 );
minColor[1] = ( mini[1] << 2 ) | ( mini[1] >> 4 );
minColor[3] = mini[3];
2012-11-26 18:58:24 +00:00
maxColor[0] = ( maxi[0] << 3 ) | ( maxi[0] >> 2 );
maxColor[1] = ( maxi[1] << 2 ) | ( maxi[1] >> 4 );
maxColor[3] = maxi[3];
2012-11-26 18:58:24 +00:00
#elif 1
2012-11-26 18:58:24 +00:00
int inset[4];
int mini[4];
int maxi[4];
inset[0] = ( maxColor[0] - minColor[0] ) - ( ( 1 << ( INSET_COLOR_SHIFT - 1 ) ) - 1 );
inset[1] = ( maxColor[1] - minColor[1] ) - ( ( 1 << ( INSET_COLOR_SHIFT - 1 ) ) - 1 );
inset[3] = ( maxColor[3] - minColor[3] ) - ( ( 1 << ( INSET_ALPHA_SHIFT - 1 ) ) - 1 );
2012-11-26 18:58:24 +00:00
mini[0] = ( ( minColor[0] << INSET_COLOR_SHIFT ) + inset[0] ) >> INSET_COLOR_SHIFT;
mini[1] = ( ( minColor[1] << INSET_COLOR_SHIFT ) + inset[1] ) >> INSET_COLOR_SHIFT;
mini[3] = ( ( minColor[3] << INSET_ALPHA_SHIFT ) + inset[3] ) >> INSET_ALPHA_SHIFT;
2012-11-26 18:58:24 +00:00
maxi[0] = ( ( maxColor[0] << INSET_COLOR_SHIFT ) - inset[0] ) >> INSET_COLOR_SHIFT;
maxi[1] = ( ( maxColor[1] << INSET_COLOR_SHIFT ) - inset[1] ) >> INSET_COLOR_SHIFT;
maxi[3] = ( ( maxColor[3] << INSET_ALPHA_SHIFT ) - inset[3] ) >> INSET_ALPHA_SHIFT;
2012-11-26 18:58:24 +00:00
mini[0] = ( mini[0] >= 0 ) ? mini[0] : 0;
mini[1] = ( mini[1] >= 0 ) ? mini[1] : 0;
mini[3] = ( mini[3] >= 0 ) ? mini[3] : 0;
2012-11-26 18:58:24 +00:00
maxi[0] = ( maxi[0] <= 255 ) ? maxi[0] : 255;
maxi[1] = ( maxi[1] <= 255 ) ? maxi[1] : 255;
maxi[3] = ( maxi[3] <= 255 ) ? maxi[3] : 255;
2012-11-26 18:58:24 +00:00
minColor[0] = byte( ( mini[0] & C565_5_MASK ) | ( mini[0] >> 5 ) );
minColor[1] = byte( ( mini[1] & C565_6_MASK ) | ( mini[1] >> 6 ) );
minColor[3] = byte( mini[3] );
2012-11-26 18:58:24 +00:00
maxColor[0] = byte( ( maxi[0] & C565_5_MASK ) | ( maxi[0] >> 5 ) );
maxColor[1] = byte( ( maxi[1] & C565_6_MASK ) | ( maxi[1] >> 6 ) );
maxColor[3] = byte( maxi[3] );
2012-11-26 18:58:24 +00:00
#endif
}
/*
========================
idDxtEncoder::InsetYCoCgAlpaBBox
========================
*/
ID_INLINE void idDxtEncoder::InsetYCoCgAlpaBBox( byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
int inset[4];
int mini[4];
int maxi[4];
inset[0] = ( maxColor[0] - minColor[0] ) - ( ( 1 << ( INSET_COLOR_SHIFT - 1 ) ) - 1 );
inset[1] = ( maxColor[1] - minColor[1] ) - ( ( 1 << ( INSET_COLOR_SHIFT - 1 ) ) - 1 );
inset[2] = ( maxColor[2] - minColor[2] ) - ( ( 1 << ( INSET_COLOR_SHIFT - 1 ) ) - 1 );
inset[3] = ( maxColor[3] - minColor[3] ) - ( ( 1 << ( INSET_ALPHA_SHIFT - 1 ) ) - 1 );
2012-11-26 18:58:24 +00:00
mini[0] = ( ( minColor[0] << INSET_COLOR_SHIFT ) + inset[0] ) >> INSET_COLOR_SHIFT;
mini[1] = ( ( minColor[1] << INSET_COLOR_SHIFT ) + inset[1] ) >> INSET_COLOR_SHIFT;
mini[2] = ( ( minColor[2] << INSET_COLOR_SHIFT ) + inset[2] ) >> INSET_COLOR_SHIFT;
mini[3] = ( ( minColor[3] << INSET_ALPHA_SHIFT ) + inset[3] ) >> INSET_ALPHA_SHIFT;
2012-11-26 18:58:24 +00:00
maxi[0] = ( ( maxColor[0] << INSET_COLOR_SHIFT ) - inset[0] ) >> INSET_COLOR_SHIFT;
maxi[1] = ( ( maxColor[1] << INSET_COLOR_SHIFT ) - inset[1] ) >> INSET_COLOR_SHIFT;
maxi[2] = ( ( maxColor[2] << INSET_COLOR_SHIFT ) - inset[2] ) >> INSET_COLOR_SHIFT;
maxi[3] = ( ( maxColor[3] << INSET_ALPHA_SHIFT ) - inset[3] ) >> INSET_ALPHA_SHIFT;
2012-11-26 18:58:24 +00:00
mini[0] = ( mini[0] >= 0 ) ? mini[0] : 0;
mini[1] = ( mini[1] >= 0 ) ? mini[1] : 0;
mini[2] = ( mini[2] >= 0 ) ? mini[2] : 0;
mini[3] = ( mini[3] >= 0 ) ? mini[3] : 0;
2012-11-26 18:58:24 +00:00
maxi[0] = ( maxi[0] <= 255 ) ? maxi[0] : 255;
maxi[1] = ( maxi[1] <= 255 ) ? maxi[1] : 255;
maxi[2] = ( maxi[2] <= 255 ) ? maxi[2] : 255;
maxi[3] = ( maxi[3] <= 255 ) ? maxi[3] : 255;
2012-11-26 18:58:24 +00:00
minColor[0] = byte( ( mini[0] & C565_5_MASK ) | ( mini[0] >> 5 ) );
minColor[1] = byte( ( mini[1] & C565_6_MASK ) | ( mini[1] >> 6 ) );
minColor[2] = byte( ( mini[2] & C565_5_MASK ) | ( mini[2] >> 5 ) );
minColor[3] = byte( mini[3] );
2012-11-26 18:58:24 +00:00
maxColor[0] = byte( ( maxi[0] & C565_5_MASK ) | ( maxi[0] >> 5 ) );
maxColor[1] = byte( ( maxi[1] & C565_6_MASK ) | ( maxi[1] >> 6 ) );
maxColor[2] = byte( ( maxi[2] & C565_5_MASK ) | ( maxi[2] >> 5 ) );
maxColor[3] = byte( maxi[3] );
}
/*
========================
idDxtEncoder::SelectYCoCgDiagonal
========================
*/
void idDxtEncoder::SelectYCoCgDiagonal( const byte* colorBlock, byte* minColor, byte* maxColor ) const
{
2012-11-26 18:58:24 +00:00
byte side = 0;
byte mid0 = byte( ( ( int ) minColor[0] + maxColor[0] + 1 ) >> 1 );
byte mid1 = byte( ( ( int ) minColor[1] + maxColor[1] + 1 ) >> 1 );
for( int i = 0; i < 16; i++ )
{
byte b0 = colorBlock[i * 4 + 0] >= mid0;
byte b1 = colorBlock[i * 4 + 1] >= mid1;
2012-11-26 18:58:24 +00:00
side += ( b0 ^ b1 );
}
2012-11-26 18:58:24 +00:00
byte mask = -( side > 8 );
2012-11-26 18:58:24 +00:00
#if defined NVIDIA_7X_HARDWARE_BUG_FIX
mask &= -( minColor[0] != maxColor[0] );
#endif
2012-11-26 18:58:24 +00:00
byte c0 = minColor[1];
byte c1 = maxColor[1];
2012-11-26 18:58:24 +00:00
c0 ^= c1;
mask &= c0;
c1 ^= mask;
c0 ^= c1;
2012-11-26 18:58:24 +00:00
minColor[1] = c0;
maxColor[1] = c1;
}
/*
========================
idDxtEncoder::CompressYCoCgDXT5Fast_Generic
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressYCoCgDXT5Fast_Generic( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
ALIGN16( byte minColor[4] );
ALIGN16( byte maxColor[4] );
2012-11-26 18:58:24 +00:00
//assert( HasConstantValuePer4x4Block( inBuf, width, height, 2 ) );
2012-11-26 18:58:24 +00:00
assert( width >= 4 && ( width & 3 ) == 0 );
assert( height >= 4 && ( height & 3 ) == 0 );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxBBox( block, minColor, maxColor );
ScaleYCoCg( block, minColor, maxColor );
InsetYCoCgBBox( minColor, maxColor );
SelectYCoCgDiagonal( block, minColor, maxColor );
2012-11-26 18:58:24 +00:00
EmitByte( maxColor[3] );
EmitByte( minColor[3] );
2012-11-26 18:58:24 +00:00
EmitAlphaIndices( block, 3, minColor[3], maxColor[3] );
2012-11-26 18:58:24 +00:00
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
// the colors are already sorted when selecting the diagonal
#endif
2012-11-26 18:58:24 +00:00
EmitUShort( ColorTo565( maxColor ) );
EmitUShort( ColorTo565( minColor ) );
2012-11-26 18:58:24 +00:00
EmitColorIndices( block, minColor, maxColor );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::CompressYCoCgAlphaDXT5Fast
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressYCoCgAlphaDXT5Fast( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
ALIGN16( byte minColor[4] );
ALIGN16( byte maxColor[4] );
2012-11-26 18:58:24 +00:00
assert( width >= 4 && ( width & 3 ) == 0 );
assert( height >= 4 && ( height & 3 ) == 0 );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
// scale down the chroma of texels that are close to gray with low luminance
for( int k = 0; k < 16; k++ )
{
if( abs( block[k * 4 + 0] - 132 ) <= 8 &&
abs( block[k * 4 + 2] - 132 ) <= 8 &&
block[k * 4 + 3] < 96 )
{
block[k * 4 + 0] = ( block[k * 4 + 0] - 132 ) / 2 + 132;
block[k * 4 + 2] = ( block[k * 4 + 2] - 132 ) / 2 + 132;
2012-11-26 18:58:24 +00:00
}
}
2012-11-26 18:58:24 +00:00
GetMinMaxBBox( block, minColor, maxColor );
InsetYCoCgAlpaBBox( minColor, maxColor );
SelectColorsDiagonal( block, minColor, maxColor );
2012-11-26 18:58:24 +00:00
EmitByte( maxColor[3] );
EmitByte( minColor[3] );
2012-11-26 18:58:24 +00:00
EmitAlphaIndices( block, 3, minColor[3], maxColor[3] );
2012-11-26 18:58:24 +00:00
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
// the colors are already sorted when selecting the diagonal
#endif
2012-11-26 18:58:24 +00:00
EmitUShort( ColorTo565( maxColor ) );
EmitUShort( ColorTo565( minColor ) );
2012-11-26 18:58:24 +00:00
EmitColorIndices( block, minColor, maxColor );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::CompressYCoCgCTX1DXT5AFast_Generic
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressYCoCgCTX1DXT5AFast_Generic( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
ALIGN16( byte minColor[4] );
ALIGN16( byte maxColor[4] );
2012-11-26 18:58:24 +00:00
assert( HasConstantValuePer4x4Block( inBuf, width, height, 2 ) );
2012-11-26 18:58:24 +00:00
assert( width >= 4 && ( width & 3 ) == 0 );
assert( height >= 4 && ( height & 3 ) == 0 );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxBBox( block, minColor, maxColor );
SelectYCoCgDiagonal( block, minColor, maxColor );
InsetColorsBBox( minColor, maxColor );
2012-11-26 18:58:24 +00:00
EmitByte( maxColor[3] );
EmitByte( minColor[3] );
2012-11-26 18:58:24 +00:00
EmitAlphaIndices( block, 3, minColor[3], maxColor[3] );
2012-11-26 18:58:24 +00:00
EmitByte( maxColor[0] );
EmitByte( maxColor[1] );
EmitByte( minColor[0] );
EmitByte( minColor[1] );
2012-11-26 18:58:24 +00:00
EmitCTX1Indices( block, minColor, maxColor );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::EmitGreenIndices
params: block - block for which to find green indices
paramO: minGreen - Min green found
paramO: maxGreen - Max green found
========================
*/
void idDxtEncoder::EmitGreenIndices( const byte* block, const int offset, const byte minGreen, const byte maxGreen )
{
2012-11-26 18:58:24 +00:00
assert( maxGreen >= minGreen );
2012-11-26 18:58:24 +00:00
const int COLOR_RANGE = 3;
2012-11-26 18:58:24 +00:00
#if 1
2012-11-26 18:58:24 +00:00
byte yb1 = ( 5 * maxGreen + 1 * minGreen + COLOR_RANGE ) / ( 2 * COLOR_RANGE );
byte yb2 = ( 3 * maxGreen + 3 * minGreen + COLOR_RANGE ) / ( 2 * COLOR_RANGE );
byte yb3 = ( 1 * maxGreen + 5 * minGreen + COLOR_RANGE ) / ( 2 * COLOR_RANGE );
2012-11-26 18:58:24 +00:00
unsigned int result = 0;
2012-11-26 18:58:24 +00:00
block += offset;
for( int i = 15; i >= 0; i-- )
{
2012-11-26 18:58:24 +00:00
result <<= 2;
byte y = block[i * 4];
2012-11-26 18:58:24 +00:00
int b1 = ( y >= yb1 );
int b2 = ( y >= yb2 );
int b3 = ( y >= yb3 );
int index = ( 4 - b1 - b2 - b3 ) & 3;
index ^= ( 2 > index );
result |= index;
}
2012-11-26 18:58:24 +00:00
EmitUInt( result );
2012-11-26 18:58:24 +00:00
#else
2012-11-26 18:58:24 +00:00
byte green[4];
2012-11-26 18:58:24 +00:00
green[0] = maxGreen;
green[1] = minGreen;
green[2] = ( 2 * green[0] + 1 * green[1] ) / 3;
green[3] = ( 1 * green[0] + 2 * green[1] ) / 3;
2012-11-26 18:58:24 +00:00
unsigned int result = 0;
2012-11-26 18:58:24 +00:00
block += offset;
for( int i = 15; i >= 0; i-- )
{
2012-11-26 18:58:24 +00:00
result <<= 2;
byte y = block[i * 4];
2012-11-26 18:58:24 +00:00
int minDist = INT_MAX;
int index;
for( int j = 0; j < 4; j++ )
{
2012-11-26 18:58:24 +00:00
int dist = abs( y - green[j] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
index = j;
}
}
result |= index;
}
2012-11-26 18:58:24 +00:00
EmitUInt( result );
2012-11-26 18:58:24 +00:00
#endif
}
/*
========================
idDxtEncoder::InsetNormalsBBoxDXT5
========================
*/
void idDxtEncoder::InsetNormalsBBoxDXT5( byte* minNormal, byte* maxNormal ) const
{
2012-11-26 18:58:24 +00:00
int inset[4];
int mini[4];
int maxi[4];
inset[3] = ( maxNormal[3] - minNormal[3] ) - ( ( 1 << ( INSET_ALPHA_SHIFT - 1 ) ) - 1 );
inset[1] = ( maxNormal[1] - minNormal[1] ) - ( ( 1 << ( INSET_COLOR_SHIFT - 1 ) ) - 1 );
2012-11-26 18:58:24 +00:00
mini[3] = ( ( minNormal[3] << INSET_ALPHA_SHIFT ) + inset[3] ) >> INSET_ALPHA_SHIFT;
mini[1] = ( ( minNormal[1] << INSET_COLOR_SHIFT ) + inset[1] ) >> INSET_COLOR_SHIFT;
2012-11-26 18:58:24 +00:00
maxi[3] = ( ( maxNormal[3] << INSET_ALPHA_SHIFT ) - inset[3] ) >> INSET_ALPHA_SHIFT;
maxi[1] = ( ( maxNormal[1] << INSET_COLOR_SHIFT ) - inset[1] ) >> INSET_COLOR_SHIFT;
2012-11-26 18:58:24 +00:00
mini[3] = ( mini[3] >= 0 ) ? mini[3] : 0;
mini[1] = ( mini[1] >= 0 ) ? mini[1] : 0;
2012-11-26 18:58:24 +00:00
maxi[3] = ( maxi[3] <= 255 ) ? maxi[3] : 255;
maxi[1] = ( maxi[1] <= 255 ) ? maxi[1] : 255;
2012-11-26 18:58:24 +00:00
minNormal[3] = byte( mini[3] );
minNormal[1] = byte( ( mini[1] & C565_6_MASK ) | ( mini[1] >> 6 ) );
2012-11-26 18:58:24 +00:00
maxNormal[3] = byte( maxi[3] );
maxNormal[1] = byte( ( maxi[1] & C565_6_MASK ) | ( maxi[1] >> 6 ) );
}
/*
========================
idDxtEncoder::InsetNormalsBBox3Dc
========================
*/
void idDxtEncoder::InsetNormalsBBox3Dc( byte* minNormal, byte* maxNormal ) const
{
int inset[4];
int mini[4];
int maxi[4];
inset[0] = ( maxNormal[0] - minNormal[0] ) - ( ( 1 << ( INSET_ALPHA_SHIFT - 1 ) ) - 1 );
inset[1] = ( maxNormal[1] - minNormal[1] ) - ( ( 1 << ( INSET_ALPHA_SHIFT - 1 ) ) - 1 );
mini[0] = ( ( minNormal[0] << INSET_ALPHA_SHIFT ) + inset[0] ) >> INSET_ALPHA_SHIFT;
mini[1] = ( ( minNormal[1] << INSET_ALPHA_SHIFT ) + inset[1] ) >> INSET_ALPHA_SHIFT;
maxi[0] = ( ( maxNormal[0] << INSET_ALPHA_SHIFT ) - inset[0] ) >> INSET_ALPHA_SHIFT;
maxi[1] = ( ( maxNormal[1] << INSET_ALPHA_SHIFT ) - inset[1] ) >> INSET_ALPHA_SHIFT;
mini[0] = ( mini[0] >= 0 ) ? mini[0] : 0;
mini[1] = ( mini[1] >= 0 ) ? mini[1] : 0;
maxi[0] = ( maxi[0] <= 255 ) ? maxi[0] : 255;
maxi[1] = ( maxi[1] <= 255 ) ? maxi[1] : 255;
minNormal[0] = ( byte )mini[0];
minNormal[1] = ( byte )mini[1];
maxNormal[0] = ( byte )maxi[0];
maxNormal[1] = ( byte )maxi[1];
2012-11-26 18:58:24 +00:00
}
/*
========================
idDxtEncoder::CompressNormalMapDXT5Fast_Generic
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressNormalMapDXT5Fast_Generic( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
ALIGN16( byte normal1[4] );
ALIGN16( byte normal2[4] );
2012-11-26 18:58:24 +00:00
assert( width >= 4 && ( width & 3 ) == 0 );
assert( height >= 4 && ( height & 3 ) == 0 );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxBBox( block, normal1, normal2 );
InsetNormalsBBoxDXT5( normal1, normal2 );
2012-11-26 18:58:24 +00:00
// Write out Nx into alpha channel.
EmitByte( normal2[3] );
EmitByte( normal1[3] );
EmitAlphaIndices( block, 3, normal1[3], normal2[3] );
2012-11-26 18:58:24 +00:00
// Write out Ny into green channel.
EmitUShort( ColorTo565( block[0], normal2[1], block[2] ) );
EmitUShort( ColorTo565( block[0], normal1[1], block[2] ) );
EmitGreenIndices( block, 1, normal1[1], normal2[1] );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::CompressImageDXN1Fast_Generic
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressImageDXN1Fast_Generic( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
ALIGN16( byte min[4] );
ALIGN16( byte max[4] );
2012-11-26 18:58:24 +00:00
assert( width >= 4 && ( width & 3 ) == 0 );
assert( height >= 4 && ( height & 3 ) == 0 );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxBBox( block, min, max );
InsetNormalsBBox3Dc( min, max );
2012-11-26 18:58:24 +00:00
// Write out an alpha channel.
EmitByte( max[0] );
EmitByte( min[0] );
EmitAlphaIndices( block, 0, min[0], max[0] );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::CompressNormalMapDXN2Fast_Generic
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::CompressNormalMapDXN2Fast_Generic( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte block[64] );
ALIGN16( byte normal1[4] );
ALIGN16( byte normal2[4] );
2012-11-26 18:58:24 +00:00
assert( width >= 4 && ( width & 3 ) == 0 );
assert( height >= 4 && ( height & 3 ) == 0 );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
for( int j = 0; j < height; j += 4, inBuf += width * 4 * 4 )
{
for( int i = 0; i < width; i += 4 )
{
2012-11-26 18:58:24 +00:00
ExtractBlock( inBuf + i * 4, width, block );
2012-11-26 18:58:24 +00:00
GetMinMaxBBox( block, normal1, normal2 );
InsetNormalsBBox3Dc( normal1, normal2 );
2012-11-26 18:58:24 +00:00
// Write out Nx as an alpha channel.
EmitByte( normal2[0] );
EmitByte( normal1[0] );
EmitAlphaIndices( block, 0, normal1[0], normal2[0] );
2012-11-26 18:58:24 +00:00
// Write out Ny as an alpha channel.
EmitByte( normal2[1] );
EmitByte( normal1[1] );
EmitAlphaIndices( block, 1, normal1[1], normal2[1] );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::DecodeDXNAlphaValues
========================
*/
void idDxtEncoder::DecodeDXNAlphaValues( const byte* inBuf, byte* values )
{
2012-11-26 18:58:24 +00:00
int i;
unsigned int indices;
byte alphas[8];
if( inBuf[0] <= inBuf[1] )
{
2012-11-26 18:58:24 +00:00
alphas[0] = inBuf[0];
alphas[1] = inBuf[1];
alphas[2] = ( 4 * alphas[0] + 1 * alphas[1] ) / 5;
alphas[3] = ( 3 * alphas[0] + 2 * alphas[1] ) / 5;
alphas[4] = ( 2 * alphas[0] + 3 * alphas[1] ) / 5;
alphas[5] = ( 1 * alphas[0] + 4 * alphas[1] ) / 5;
alphas[6] = 0;
alphas[7] = 255;
}
else
{
2012-11-26 18:58:24 +00:00
alphas[0] = inBuf[0];
alphas[1] = inBuf[1];
alphas[2] = ( 6 * alphas[0] + 1 * 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] = ( 1 * alphas[0] + 6 * alphas[1] ) / 7;
}
indices = ( int )inBuf[2] | ( ( int )inBuf[3] << 8 ) | ( ( int )inBuf[4] << 16 );
for( i = 0; i < 8; i++ )
{
2012-11-26 18:58:24 +00:00
values[i] = alphas[indices & 7];
indices >>= 3;
}
indices = ( int )inBuf[5] | ( ( int )inBuf[6] << 8 ) | ( ( int )inBuf[7] << 16 );
for( i = 8; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
values[i] = alphas[indices & 7];
indices >>= 3;
}
}
/*
========================
idDxtEncoder::EncodeNormalRGBIndices
params: values - 16 normal block for which to find normal Y indices
paramO: min - Min grayscale value
paramO: max - Max grayscale value
========================
*/
void idDxtEncoder::EncodeNormalRGBIndices( byte* outBuf, const byte min, const byte max, const byte* values )
{
2012-11-26 18:58:24 +00:00
const int COLOR_RANGE = 3;
2012-11-26 18:58:24 +00:00
byte maskedMin, maskedMax, mid, yb1, yb2, yb3;
2012-11-26 18:58:24 +00:00
maskedMax = max & C565_6_MASK;
maskedMin = min & C565_6_MASK;
mid = ( maskedMax - maskedMin ) / ( 2 * COLOR_RANGE );
2012-11-26 18:58:24 +00:00
yb1 = maskedMax - mid;
yb2 = ( 2 * maskedMax + 1 * maskedMin ) / COLOR_RANGE - mid;
yb3 = ( 1 * maskedMax + 2 * maskedMin ) / COLOR_RANGE - mid;
2012-11-26 18:58:24 +00:00
unsigned int result = 0;
for( int i = 15; i >= 0; i-- )
{
2012-11-26 18:58:24 +00:00
result <<= 2;
byte y = values[i];
int b1 = ( y >= yb1 );
int b2 = ( y >= yb2 );
int b3 = ( y >= yb3 );
int index = ( 4 - b1 - b2 - b3 ) & 3;
index ^= ( 2 > index );
result |= index;
}
unsigned short maskedMax5 = ( max & C565_5_MASK ) >> 3;
unsigned short maskedMin5 = ( min & C565_5_MASK ) >> 3;
unsigned short smax = ( maskedMax5 << 11 ) | ( maskedMax << 3 ) | maskedMax5;
unsigned short smin = ( maskedMin5 << 11 ) | ( maskedMin << 3 ) | maskedMin5;
2012-11-26 18:58:24 +00:00
outBuf[0] = byte( ( smax >> 0 ) & 0xFF );
outBuf[1] = byte( ( smax >> 8 ) & 0xFF );
outBuf[2] = byte( ( smin >> 0 ) & 0xFF );
outBuf[3] = byte( ( smin >> 8 ) & 0xFF );
2012-11-26 18:58:24 +00:00
outBuf[4] = byte( ( result >> 0 ) & 0xFF );
outBuf[5] = byte( ( result >> 8 ) & 0xFF );
outBuf[6] = byte( ( result >> 16 ) & 0xFF );
outBuf[7] = byte( ( result >> 24 ) & 0xFF );
}
/*
========================
idDxtEncoder::ConvertNormalMapDXN2_DXT5
params: inBuf - normal map compressed in DXN2 format
paramO: outBuf - result of compression in DXT5 format
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::ConvertNormalMapDXN2_DXT5( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte values[16] );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
assert( 0 );
return;
}
for( int j = 0; j < height; j += 4 )
{
for( int i = 0; i < width; i += 4, inBuf += 16, outBuf += 16 )
{
2012-11-26 18:58:24 +00:00
// decode normal Y stored as a DXT5 alpha channel
DecodeDXNAlphaValues( inBuf + 0, values );
2012-11-26 18:58:24 +00:00
// copy normal X
memcpy( outBuf + 0, inBuf + 8, 8 );
2012-11-26 18:58:24 +00:00
// get the min/max Y
byte minNormalY = 255;
byte maxNormalY = 0;
for( int i = 0; i < 16; i++ )
{
if( values[i] < minNormalY )
{
2012-11-26 18:58:24 +00:00
minNormalY = values[i];
}
if( values[i] > maxNormalY )
{
2012-11-26 18:58:24 +00:00
maxNormalY = values[i];
}
}
2012-11-26 18:58:24 +00:00
// encode normal Y into DXT5 color channels
EncodeNormalRGBIndices( outBuf + 8, minNormalY, maxNormalY, values );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::DecodeNormalYValues
========================
*/
void idDxtEncoder::DecodeNormalYValues( const byte* inBuf, byte& min, byte& max, byte* values )
{
2012-11-26 18:58:24 +00:00
int i;
unsigned int indexes;
unsigned short normal0, normal1;
byte normalsY[4];
normal0 = inBuf[0] | ( inBuf[1] << 8 );
normal1 = inBuf[2] | ( inBuf[3] << 8 );
2012-11-26 18:58:24 +00:00
assert( normal0 >= normal1 );
2012-11-26 18:58:24 +00:00
normalsY[0] = GreenFrom565( normal0 );
normalsY[1] = GreenFrom565( normal1 );
normalsY[2] = ( 2 * normalsY[0] + 1 * normalsY[1] ) / 3;
normalsY[3] = ( 1 * normalsY[0] + 2 * normalsY[1] ) / 3;
indexes = ( unsigned int )inBuf[4] | ( ( unsigned int )inBuf[5] << 8 ) | ( ( unsigned int )inBuf[6] << 16 ) | ( ( unsigned int )inBuf[7] << 24 );
for( i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
values[i] = normalsY[indexes & 3];
indexes >>= 2;
}
2012-11-26 18:58:24 +00:00
max = normalsY[0];
min = normalsY[1];
}
/*
========================
idDxtEncoder::EncodeDXNAlphaValues
========================
*/
void idDxtEncoder::EncodeDXNAlphaValues( byte* outBuf, const byte min, const byte max, const byte* values )
{
2012-11-26 18:58:24 +00:00
int i;
byte alphas[8];
int j;
unsigned int indexes[16];
2012-11-26 18:58:24 +00:00
alphas[0] = max;
alphas[1] = min;
alphas[2] = ( 6 * alphas[0] + 1 * 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] = ( 1 * alphas[0] + 6 * alphas[1] ) / 7;
2012-11-26 18:58:24 +00:00
int error = 0;
for( i = 0; i < 16; i++ )
{
2012-11-26 18:58:24 +00:00
int minDist = MAX_TYPE( int );
byte a = values[i];
for( j = 0; j < 8; j++ )
{
2012-11-26 18:58:24 +00:00
int dist = AlphaDistance( a, alphas[j] );
if( dist < minDist )
{
2012-11-26 18:58:24 +00:00
minDist = dist;
indexes[i] = j;
}
}
error += minDist;
}
2012-11-26 18:58:24 +00:00
outBuf[0] = max;
outBuf[1] = min;
outBuf[2] = byte( ( indexes[ 0] >> 0 ) | ( indexes[ 1] << 3 ) | ( indexes[ 2] << 6 ) );
outBuf[3] = byte( ( indexes[ 2] >> 2 ) | ( indexes[ 3] << 1 ) | ( indexes[ 4] << 4 ) | ( indexes[ 5] << 7 ) );
outBuf[4] = byte( ( indexes[ 5] >> 1 ) | ( indexes[ 6] << 2 ) | ( indexes[ 7] << 5 ) );
outBuf[5] = byte( ( indexes[ 8] >> 0 ) | ( indexes[ 9] << 3 ) | ( indexes[10] << 6 ) );
outBuf[6] = byte( ( indexes[10] >> 2 ) | ( indexes[11] << 1 ) | ( indexes[12] << 4 ) | ( indexes[13] << 7 ) );
outBuf[7] = byte( ( indexes[13] >> 1 ) | ( indexes[14] << 2 ) | ( indexes[15] << 5 ) );
2012-11-26 18:58:24 +00:00
}
/*
========================
idDxtEncoder::ConvertNormalMapDXT5_DXN2
params: inBuf - image to compress
paramO: outBuf - result of compression
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::ConvertNormalMapDXT5_DXN2( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte values[16] );
byte minNormalY, maxNormalY;
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
assert( 0 );
return;
}
for( int j = 0; j < height; j += 4 )
{
for( int i = 0; i < width; i += 4, inBuf += 16, outBuf += 16 )
{
2012-11-26 18:58:24 +00:00
// decode normal Y stored as a DXT5 alpha channel
DecodeNormalYValues( inBuf + 8, minNormalY, maxNormalY, values );
2012-11-26 18:58:24 +00:00
memcpy( outBuf + 8, inBuf + 0, 8 );
2012-11-26 18:58:24 +00:00
// encode normal Y into DXT5 green channel
EncodeDXNAlphaValues( outBuf + 0, minNormalY, maxNormalY, values );
}
outData += dstPadding;
inBuf += srcPadding;
}
}
/*
========================
idDxtEncoder::ConvertImageDXN1_DXT1
params: inBuf - normal map compressed in DXN1 format
paramO: outBuf - result of compression in DXT1 format
params: width - width of image
params: height - height of image
========================
*/
void idDxtEncoder::ConvertImageDXN1_DXT1( const byte* inBuf, byte* outBuf, int width, int height )
{
2012-11-26 18:58:24 +00:00
ALIGN16( byte values[16] );
2012-11-26 18:58:24 +00:00
this->width = width;
this->height = height;
this->outData = outBuf;
if( width > 4 && ( width & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( height > 4 && ( height & 3 ) != 0 )
{
2012-11-26 18:58:24 +00:00
return;
}
if( width < 4 || height < 4 )
{
2012-11-26 18:58:24 +00:00
assert( 0 );
return;
}
for( int j = 0; j < height; j += 4 )
{
for( int i = 0; i < width; i += 4, inBuf += 8, outBuf += 8 )
{
2012-11-26 18:58:24 +00:00
// decode single channel stored as a DXT5 alpha channel
DecodeDXNAlphaValues( inBuf + 0, values );
2012-11-26 18:58:24 +00:00
// get the min/max
byte min = 255;
byte max = 0;
for( int i = 0; i < 16; i++ )
{
if( values[i] < min )
{
2012-11-26 18:58:24 +00:00
min = values[i];
}
if( values[i] > max )
{
2012-11-26 18:58:24 +00:00
max = values[i];
}
}
2012-11-26 18:58:24 +00:00
// encode single channel into DXT1
EncodeNormalRGBIndices( outBuf + 0, min, max, values );
}
outData += dstPadding;
inBuf += srcPadding;
}
}