mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-02 08:51:57 +00:00
5212 lines
134 KiB
C++
5212 lines
134 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
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
|
|
{
|
|
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;
|
|
min ^= max;
|
|
max ^= ( min & mask );
|
|
min ^= max;
|
|
*( int* )minColor = min;
|
|
*( int* )maxColor = max;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::HasConstantValuePer4x4Block
|
|
========================
|
|
*/
|
|
bool idDxtEncoder::HasConstantValuePer4x4Block( const byte* inBuf, int width, int height, int channel ) const
|
|
{
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
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;
|
|
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 )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
inBuf += srcPadding;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::WriteTinyColorDXT1
|
|
========================
|
|
*/
|
|
void idDxtEncoder::WriteTinyColorDXT1( const byte* inBuf, int width, int height )
|
|
{
|
|
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++ )
|
|
{
|
|
// 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
|
|
|
|
inBuf += stride;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::WriteTinyColorDXT5
|
|
========================
|
|
*/
|
|
void idDxtEncoder::WriteTinyColorDXT5( const byte* inBuf, int width, int height )
|
|
{
|
|
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++ )
|
|
{
|
|
// 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 );
|
|
|
|
EmitUShort( ColorTo565( inBuf ) );
|
|
EmitUShort( 0 ); // dummy, never used
|
|
EmitUInt( 0 ); // 4 color index bytes all use the first color
|
|
|
|
inBuf += stride;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::WriteTinyColorCTX1DXT5A
|
|
========================
|
|
*/
|
|
void idDxtEncoder::WriteTinyColorCTX1DXT5A( const byte* inBuf, int width, int height )
|
|
{
|
|
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++ )
|
|
{
|
|
// 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
|
|
|
|
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 );
|
|
|
|
inBuf += stride;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::WriteTinyNormalMapDXT5
|
|
========================
|
|
*/
|
|
void idDxtEncoder::WriteTinyNormalMapDXT5( const byte* inBuf, int width, int height )
|
|
{
|
|
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++ )
|
|
{
|
|
// 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 );
|
|
|
|
EmitUShort( ColorTo565( inBuf[0], inBuf[1], inBuf[2] ) );
|
|
EmitUShort( 0 ); // dummy, never used
|
|
EmitUInt( 0 ); // 4 color index bytes all use the first color
|
|
|
|
inBuf += stride;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::WriteTinyNormalMapDXN
|
|
========================
|
|
*/
|
|
void idDxtEncoder::WriteTinyNormalMapDXN( const byte* inBuf, int width, int height )
|
|
{
|
|
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++ )
|
|
{
|
|
// 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 );
|
|
|
|
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 );
|
|
|
|
inBuf += stride;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::WriteTinyDXT5A
|
|
========================
|
|
*/
|
|
void idDxtEncoder::WriteTinyDXT5A( const byte* inBuf, int width, int height )
|
|
{
|
|
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++ )
|
|
{
|
|
// 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 );
|
|
|
|
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 );
|
|
inPtr += width * 4;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
SwapColors
|
|
========================
|
|
*/
|
|
void SwapColors( byte* c1, byte* c2 )
|
|
{
|
|
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
|
|
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
|
|
{
|
|
int maxDistC = -1;
|
|
int maxDistA = -1;
|
|
|
|
for( int i = 0; i < 64 - 4; i += 4 )
|
|
{
|
|
for( int j = i + 4; j < 64; j += 4 )
|
|
{
|
|
int dc = ColorDistance( &colorBlock[i], &colorBlock[j] );
|
|
if( dc > maxDistC )
|
|
{
|
|
maxDistC = dc;
|
|
memcpy( minColor, colorBlock + i, 3 );
|
|
memcpy( maxColor, colorBlock + j, 3 );
|
|
}
|
|
int da = AlphaDistance( colorBlock[i + 3], colorBlock[j + 3] );
|
|
if( da > maxDistA )
|
|
{
|
|
maxDistA = da;
|
|
minColor[3] = colorBlock[i + 3];
|
|
maxColor[3] = colorBlock[j + 3];
|
|
}
|
|
}
|
|
}
|
|
if( maxColor[0] < minColor[0] )
|
|
{
|
|
SwapColors( minColor, maxColor );
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::GetMinMaxColorsLuminance
|
|
|
|
Finds the two RGB colors in a 4x4 block furthest apart based on luminance. Also finds the two
|
|
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
|
|
{
|
|
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 )
|
|
{
|
|
maxLumC = luminance;
|
|
memcpy( maxColor, colorBlock + i * 4, 3 );
|
|
}
|
|
if( luminance < minLumC )
|
|
{
|
|
minLumC = luminance;
|
|
memcpy( minColor, colorBlock + i * 4, 3 );
|
|
}
|
|
int alpha = colorBlock[i * 4 + 3];
|
|
if( alpha > maxAlpha )
|
|
{
|
|
maxAlpha = alpha;
|
|
maxColor[3] = ( byte )alpha;
|
|
}
|
|
if( alpha < minAlpha )
|
|
{
|
|
minAlpha = alpha;
|
|
minColor[3] = ( byte )alpha;
|
|
}
|
|
}
|
|
if( maxColor[0] < minColor[0] )
|
|
{
|
|
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
|
|
{
|
|
int i, j;
|
|
byte alphas[8];
|
|
|
|
alphas[0] = maxAlpha;
|
|
alphas[1] = minAlpha;
|
|
|
|
if( maxAlpha > minAlpha )
|
|
{
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
int error = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
|
|
byte a = colorBlock[i * 4 + alphaOffset];
|
|
for( j = 0; j < 8; j++ )
|
|
{
|
|
unsigned int dist = AlphaDistance( a, alphas[j] );
|
|
if( dist < minDist )
|
|
{
|
|
minDist = dist;
|
|
}
|
|
}
|
|
error += minDist;
|
|
|
|
if( error >= lastError )
|
|
{
|
|
return error;
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
int i, j;
|
|
byte alphaMin, alphaMax;
|
|
int error, bestError = MAX_TYPE( int );
|
|
|
|
alphaMin = 255;
|
|
alphaMax = 0;
|
|
|
|
// get alpha min / max
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
if( colorBlock[i * 4 + alphaOffset] < alphaMin )
|
|
{
|
|
alphaMin = colorBlock[i * 4 + alphaOffset];
|
|
}
|
|
if( colorBlock[i * 4 + alphaOffset] > alphaMax )
|
|
{
|
|
alphaMax = colorBlock[i * 4 + alphaOffset];
|
|
}
|
|
}
|
|
|
|
const int ALPHA_EXPAND = 32;
|
|
|
|
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 )
|
|
{
|
|
bestError = error;
|
|
minColor[alphaOffset] = ( byte )i;
|
|
maxColor[alphaOffset] = ( byte )j;
|
|
}
|
|
|
|
error = GetSquareAlphaError( colorBlock, alphaOffset, ( byte )j, ( byte )i, bestError );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
minColor[alphaOffset] = ( byte )i;
|
|
maxColor[alphaOffset] = ( byte )j;
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
int i, j;
|
|
byte colors[4][4];
|
|
|
|
ColorFrom565( color0, colors[0] );
|
|
ColorFrom565( color1, colors[1] );
|
|
|
|
if( color0 > color1 )
|
|
{
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
int error = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
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 )
|
|
{
|
|
minDist = dist;
|
|
}
|
|
}
|
|
// accumulated error
|
|
error += minDist;
|
|
|
|
if( error > lastError )
|
|
{
|
|
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
|
|
{
|
|
int i, j;
|
|
byte colors[4][4];
|
|
|
|
ColorFrom565( color0, colors[0] );
|
|
ColorFrom565( color1, colors[1] );
|
|
|
|
if( color0 > color1 )
|
|
{
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
int error = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
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;
|
|
unsigned int dist = idMath::Ftoi( ( r - s ) * ( r - s ) );
|
|
if( dist < minDist )
|
|
{
|
|
minDist = dist;
|
|
}
|
|
}
|
|
// accumulated error
|
|
error += minDist;
|
|
|
|
if( error > lastError )
|
|
{
|
|
return error;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::GetMinMaxColorsHQ
|
|
|
|
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
|
|
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
|
|
{
|
|
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 );
|
|
|
|
bboxMin[0] = bboxMin[1] = bboxMin[2] = 255;
|
|
bboxMax[0] = bboxMax[1] = bboxMax[2] = 0;
|
|
|
|
// get color bbox
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
if( colorBlock[i * 4 + 0] < bboxMin[0] )
|
|
{
|
|
bboxMin[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] < bboxMin[1] )
|
|
{
|
|
bboxMin[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] < bboxMin[2] )
|
|
{
|
|
bboxMin[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
if( colorBlock[i * 4 + 0] > bboxMax[0] )
|
|
{
|
|
bboxMax[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] > bboxMax[1] )
|
|
{
|
|
bboxMax[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] > bboxMax[2] )
|
|
{
|
|
bboxMax[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
}
|
|
|
|
// decrease range for 565 encoding
|
|
bboxMin[0] >>= 3;
|
|
bboxMin[1] >>= 2;
|
|
bboxMin[2] >>= 3;
|
|
bboxMax[0] >>= 3;
|
|
bboxMax[1] >>= 2;
|
|
bboxMax[2] >>= 3;
|
|
|
|
// get the minimum distance the end points of the line must be apart along each axis
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
minAxisDist[i] = ( bboxMax[i] - bboxMin[i] );
|
|
if( minAxisDist[i] >= 16 )
|
|
{
|
|
minAxisDist[i] = minAxisDist[i] * 3 / 4;
|
|
}
|
|
else if( minAxisDist[i] >= 8 )
|
|
{
|
|
minAxisDist[i] = minAxisDist[i] * 2 / 4;
|
|
}
|
|
else if( minAxisDist[i] >= 4 )
|
|
{
|
|
minAxisDist[i] = minAxisDist[i] * 1 / 4;
|
|
}
|
|
else
|
|
{
|
|
minAxisDist[i] = 0;
|
|
}
|
|
}
|
|
|
|
// expand the bounding box
|
|
const int C565_BBOX_EXPAND = 1;
|
|
|
|
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;
|
|
|
|
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] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
|
|
{
|
|
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
|
|
{
|
|
if( abs( i1 - j1 ) < minAxisDist[1] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for( i2 = bboxMin[2]; i2 <= bboxMax[2]; i2++ )
|
|
{
|
|
for( j2 = bboxMax[2]; j2 >= bboxMin[2]; j2-- )
|
|
{
|
|
if( abs( i2 - j2 ) < minAxisDist[2] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
minColor565 = ( unsigned short )( ( i0 << 11 ) | ( i1 << 5 ) | ( i2 << 0 ) );
|
|
maxColor565 = ( unsigned short )( ( j0 << 11 ) | ( j1 << 5 ) | ( j2 << 0 ) );
|
|
|
|
if( !noBlack )
|
|
{
|
|
error = GetSquareColorsError( colorBlock, maxColor565, minColor565, bestError );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
bestMinColor565 = minColor565;
|
|
bestMaxColor565 = maxColor565;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( minColor565 <= maxColor565 )
|
|
{
|
|
SwapValues( minColor565, maxColor565 );
|
|
}
|
|
}
|
|
|
|
error = GetSquareColorsError( colorBlock, minColor565, maxColor565, bestError );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
bestMinColor565 = minColor565;
|
|
bestMaxColor565 = maxColor565;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ColorFrom565( bestMinColor565, minColor );
|
|
ColorFrom565( bestMaxColor565, maxColor );
|
|
|
|
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
|
|
{
|
|
int i, j;
|
|
byte colors[4][4];
|
|
|
|
colors[0][0] = color0[0];
|
|
colors[0][1] = color0[1];
|
|
colors[1][0] = color1[0];
|
|
colors[1][1] = color1[1];
|
|
|
|
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;
|
|
|
|
int error = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
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 )
|
|
{
|
|
minDist = dist;
|
|
}
|
|
}
|
|
// accumulated error
|
|
error += minDist;
|
|
|
|
if( error > lastError )
|
|
{
|
|
return error;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::GetMinMaxCTX1HQ
|
|
|
|
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
|
|
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
|
|
{
|
|
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 );
|
|
|
|
bboxMin[0] = bboxMin[1] = 255;
|
|
bboxMax[0] = bboxMax[1] = 0;
|
|
|
|
// get color bbox
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
if( colorBlock[i * 4 + 0] < bboxMin[0] )
|
|
{
|
|
bboxMin[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] < bboxMin[1] )
|
|
{
|
|
bboxMin[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 0] > bboxMax[0] )
|
|
{
|
|
bboxMax[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] > bboxMax[1] )
|
|
{
|
|
bboxMax[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
}
|
|
|
|
// get the minimum distance the end points of the line must be apart along each axis
|
|
for( i = 0; i < 2; i++ )
|
|
{
|
|
minAxisDist[i] = ( bboxMax[i] - bboxMin[i] );
|
|
if( minAxisDist[i] >= 64 )
|
|
{
|
|
minAxisDist[i] = minAxisDist[i] * 3 / 4;
|
|
}
|
|
else if( minAxisDist[i] >= 32 )
|
|
{
|
|
minAxisDist[i] = minAxisDist[i] * 2 / 4;
|
|
}
|
|
else if( minAxisDist[i] >= 16 )
|
|
{
|
|
minAxisDist[i] = minAxisDist[i] * 1 / 4;
|
|
}
|
|
else
|
|
{
|
|
minAxisDist[i] = 0;
|
|
}
|
|
}
|
|
|
|
// expand the bounding box
|
|
const int CXT1_BBOX_EXPAND = 6;
|
|
|
|
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] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
|
|
{
|
|
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
|
|
{
|
|
if( abs( i1 - j1 ) < minAxisDist[1] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
curMinColor[0] = ( byte )i0;
|
|
curMinColor[1] = ( byte )i1;
|
|
|
|
curMaxColor[0] = ( byte )j0;
|
|
curMaxColor[1] = ( byte )j1;
|
|
|
|
error = GetSquareCTX1Error( colorBlock, curMinColor, curMaxColor, bestError );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
memcpy( minColor, curMinColor, 2 );
|
|
memcpy( maxColor, curMaxColor, 2 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestError;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::GetMinMaxNormalYHQ
|
|
|
|
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
|
|
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
|
|
{
|
|
unsigned short bestMinColor565, bestMaxColor565;
|
|
byte bboxMin[3], bboxMax[3];
|
|
int error, bestError = MAX_TYPE( int );
|
|
|
|
bboxMin[1] = 255;
|
|
bboxMax[1] = 0;
|
|
|
|
// get color bbox
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
if( colorBlock[i * 4 + 1] < bboxMin[1] )
|
|
{
|
|
bboxMin[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 1] > bboxMax[1] )
|
|
{
|
|
bboxMax[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
}
|
|
|
|
// decrease range for 565 encoding
|
|
bboxMin[1] >>= 2;
|
|
bboxMax[1] >>= 2;
|
|
|
|
// expand the bounding box
|
|
const int C565_BBOX_EXPAND = 1;
|
|
|
|
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;
|
|
|
|
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 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
unsigned short minColor565 = ( unsigned short )i1 << 5;
|
|
unsigned short maxColor565 = ( unsigned short )j1 << 5;
|
|
|
|
if( !noBlack )
|
|
{
|
|
error = GetSquareNormalYError( colorBlock, maxColor565, minColor565, bestError, scale );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
bestMinColor565 = minColor565;
|
|
bestMaxColor565 = maxColor565;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( minColor565 <= maxColor565 )
|
|
{
|
|
SwapValues( minColor565, maxColor565 );
|
|
}
|
|
}
|
|
|
|
error = GetSquareNormalYError( colorBlock, minColor565, maxColor565, bestError, scale );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
bestMinColor565 = minColor565;
|
|
bestMaxColor565 = maxColor565;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
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)
|
|
int result;
|
|
__asm
|
|
{
|
|
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
|
|
|
|
rsqrtps xmm1, xmm2
|
|
mulps xmm2, xmm1
|
|
mulps xmm2, xmm1
|
|
subps xmm2, SIMD_SP_rsqrt_c0
|
|
mulps xmm1, SIMD_SP_rsqrt_c1
|
|
mulps xmm2, xmm1
|
|
|
|
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)
|
|
}
|
|
|
|
/*
|
|
========================
|
|
NormalDistanceDXT5
|
|
========================
|
|
*/
|
|
int NormalDistanceDXT5( const int* vector, const int* normalized )
|
|
{
|
|
#if _MSC_VER && defined(_M_IX86)
|
|
int result;
|
|
__asm
|
|
{
|
|
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
|
|
|
|
rsqrtps xmm1, xmm2
|
|
mulps xmm2, xmm1
|
|
mulps xmm2, xmm1
|
|
subps xmm2, SIMD_SP_rsqrt_c0
|
|
mulps xmm1, SIMD_SP_rsqrt_c1
|
|
mulps xmm2, xmm1
|
|
|
|
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)
|
|
}
|
|
|
|
/*
|
|
========================
|
|
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
|
|
{
|
|
byte byteColors[2][4];
|
|
ALIGN16( int colors[4][4] );
|
|
|
|
ColorFrom565( color0, byteColors[0] );
|
|
ColorFrom565( color1, byteColors[1] );
|
|
|
|
for( int i = 0; i < 4; i++ )
|
|
{
|
|
colors[0][i] = byteColors[0][i];
|
|
colors[1][i] = byteColors[1][i];
|
|
}
|
|
|
|
if( color0 > color1 )
|
|
{
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
int error = 0;
|
|
int tempColorIndices[16];
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
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 )
|
|
{
|
|
minDist = dist;
|
|
tempColorIndices[i] = j;
|
|
}
|
|
}
|
|
// accumulated error
|
|
error += minDist;
|
|
|
|
if( error > lastError )
|
|
{
|
|
return error;
|
|
}
|
|
}
|
|
|
|
colorIndices = 0;
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
colorIndices |= ( tempColorIndices[i] << ( unsigned int )( i << 1 ) );
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::GetMinMaxNormalsDXT1HQ
|
|
|
|
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
|
|
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
|
|
{
|
|
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] );
|
|
|
|
bboxMin[0] = bboxMin[1] = bboxMin[2] = 128;
|
|
bboxMax[0] = bboxMax[1] = bboxMax[2] = 128;
|
|
|
|
// get color bbox
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
if( colorBlock[i * 4 + 0] < bboxMin[0] )
|
|
{
|
|
bboxMin[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] < bboxMin[1] )
|
|
{
|
|
bboxMin[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] < bboxMin[2] )
|
|
{
|
|
bboxMin[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
if( colorBlock[i * 4 + 0] > bboxMax[0] )
|
|
{
|
|
bboxMax[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] > bboxMax[1] )
|
|
{
|
|
bboxMax[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] > bboxMax[2] )
|
|
{
|
|
bboxMax[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < 64; i++ )
|
|
{
|
|
intColorBlock[i] = colorBlock[i];
|
|
}
|
|
|
|
// decrease range for 565 encoding
|
|
bboxMin[0] >>= 3;
|
|
bboxMin[1] >>= 2;
|
|
bboxMin[2] >>= 3;
|
|
bboxMax[0] >>= 3;
|
|
bboxMax[1] >>= 2;
|
|
bboxMax[2] >>= 3;
|
|
|
|
// get the minimum distance the end points of the line must be apart along each axis
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
minAxisDist[i] = 0;
|
|
}
|
|
|
|
// expand the bounding box
|
|
const int C565_BBOX_EXPAND = 2;
|
|
|
|
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] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
|
|
{
|
|
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
|
|
{
|
|
if( abs( i1 - j1 ) < minAxisDist[1] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for( i2 = bboxMin[2]; i2 <= bboxMax[2]; i2++ )
|
|
{
|
|
for( j2 = bboxMax[2]; j2 >= bboxMin[2]; j2-- )
|
|
{
|
|
if( abs( i2 - j2 ) < minAxisDist[2] )
|
|
{
|
|
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 )
|
|
{
|
|
error = GetSquareNormalsDXT1Error( intColorBlock, maxColor565, minColor565, bestError, tempColorIndices );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
bestMinColor565 = minColor565;
|
|
bestMaxColor565 = maxColor565;
|
|
colorIndices = tempColorIndices;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( minColor565 <= maxColor565 )
|
|
{
|
|
SwapValues( minColor565, maxColor565 );
|
|
}
|
|
}
|
|
|
|
error = GetSquareNormalsDXT1Error( intColorBlock, minColor565, maxColor565, bestError, tempColorIndices );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
bestMinColor565 = minColor565;
|
|
bestMaxColor565 = maxColor565;
|
|
colorIndices = tempColorIndices;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ColorFrom565( bestMinColor565, minColor );
|
|
ColorFrom565( bestMaxColor565, maxColor );
|
|
|
|
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
|
|
{
|
|
byte alphas[8];
|
|
byte colors[4][4];
|
|
|
|
unsigned short smin = ColorTo565( minNormal );
|
|
unsigned short smax = ColorTo565( maxNormal );
|
|
|
|
ColorFrom565( smax, colors[0] );
|
|
ColorFrom565( smin, colors[1] );
|
|
|
|
if( smax > smin )
|
|
{
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
alphas[0] = maxNormal[3];
|
|
alphas[1] = minNormal[3];
|
|
|
|
if( maxNormal[3] > minNormal[3] )
|
|
{
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
int error = 0;
|
|
int tempColorIndices[16];
|
|
int tempAlphaIndices[16];
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
ALIGN16( int normal[4] );
|
|
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
|
|
|
|
for( int j = 0; j < 4; j++ )
|
|
{
|
|
normal[0] = colors[j][0];
|
|
normal[1] = colors[j][1];
|
|
normal[2] = colors[j][2];
|
|
|
|
for( int k = 0; k < 8; k++ )
|
|
{
|
|
normal[3] = alphas[k];
|
|
unsigned int dist = NormalDistanceDXT5( normal, &normalBlock[i * 4] );
|
|
if( dist < minDist )
|
|
{
|
|
minDist = dist;
|
|
tempColorIndices[i] = j;
|
|
tempAlphaIndices[i] = k;
|
|
}
|
|
}
|
|
}
|
|
error += minDist;
|
|
|
|
if( error >= lastError )
|
|
{
|
|
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 ) );
|
|
|
|
colorIndices = 0;
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
colorIndices |= ( tempColorIndices[i] << ( unsigned int )( i << 1 ) );
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::GetMinMaxNormalsDXT5HQ
|
|
|
|
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
|
|
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
|
|
{
|
|
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] );
|
|
|
|
bboxMin[0] = bboxMin[1] = bboxMin[2] = bboxMin[3] = 255;
|
|
bboxMax[0] = bboxMax[1] = bboxMax[2] = bboxMax[3] = 0;
|
|
|
|
// get color bbox
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
if( colorBlock[i * 4 + 0] < bboxMin[0] )
|
|
{
|
|
bboxMin[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] < bboxMin[1] )
|
|
{
|
|
bboxMin[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] < bboxMin[2] )
|
|
{
|
|
bboxMin[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
if( colorBlock[i * 4 + 3] < bboxMin[3] )
|
|
{
|
|
bboxMin[3] = colorBlock[i * 4 + 3];
|
|
}
|
|
if( colorBlock[i * 4 + 0] > bboxMax[0] )
|
|
{
|
|
bboxMax[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] > bboxMax[1] )
|
|
{
|
|
bboxMax[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] > bboxMax[2] )
|
|
{
|
|
bboxMax[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
if( colorBlock[i * 4 + 3] > bboxMax[3] )
|
|
{
|
|
bboxMax[3] = colorBlock[i * 4 + 3];
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < 64; i++ )
|
|
{
|
|
intColorBlock[i] = colorBlock[i];
|
|
}
|
|
|
|
// decrease range for 565 encoding
|
|
bboxMin[0] >>= 3;
|
|
bboxMin[1] >>= 2;
|
|
bboxMax[0] >>= 3;
|
|
bboxMax[1] >>= 2;
|
|
|
|
// get the minimum distance the end points of the line must be apart along each axis
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
minAxisDist[i] = 0;
|
|
}
|
|
|
|
// expand the bounding box
|
|
const int C565_BBOX_EXPAND = 2;
|
|
const int ALPHA_BBOX_EXPAND = 32;
|
|
|
|
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] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
|
|
{
|
|
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
|
|
{
|
|
if( abs( i1 - j1 ) < minAxisDist[1] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
tmin[0] = ( byte )j0 << 3;
|
|
tmin[1] = ( byte )j1 << 2;
|
|
tmin[2] = 0;
|
|
|
|
tmax[0] = ( byte )i0 << 3;
|
|
tmax[1] = ( byte )i1 << 2;
|
|
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] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
tmin[3] = ( byte )j3;
|
|
tmax[3] = ( byte )i3;
|
|
|
|
error = GetSquareNormalsDXT5Error( intColorBlock, tmin, tmax, bestError, tempColorIndices, tempAlphaIndices );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
memcpy( minColor, tmin, 4 );
|
|
memcpy( maxColor, tmax, 4 );
|
|
colorIndices = tempColorIndices;
|
|
memcpy( alphaIndices, tempAlphaIndices, 6 );
|
|
}
|
|
|
|
tmin[3] = ( byte )i3;
|
|
tmax[3] = ( byte )j3;
|
|
|
|
error = GetSquareNormalsDXT5Error( intColorBlock, tmin, tmax, bestError, tempColorIndices, tempAlphaIndices );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
memcpy( minColor, tmin, 4 );
|
|
memcpy( maxColor, tmax, 4 );
|
|
colorIndices = tempColorIndices;
|
|
memcpy( alphaIndices, tempAlphaIndices, 6 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestError;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::GetMinMaxNormalsDXT5HQFast
|
|
|
|
Uses an exhaustive search to find the two RGB colors that produce the least error when used to
|
|
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
|
|
{
|
|
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] );
|
|
|
|
bboxMin[0] = bboxMin[1] = bboxMin[2] = bboxMin[3] = 255;
|
|
bboxMax[0] = bboxMax[1] = bboxMax[2] = bboxMax[3] = 0;
|
|
|
|
// get color bbox
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
if( colorBlock[i * 4 + 0] < bboxMin[0] )
|
|
{
|
|
bboxMin[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] < bboxMin[1] )
|
|
{
|
|
bboxMin[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] < bboxMin[2] )
|
|
{
|
|
bboxMin[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
if( colorBlock[i * 4 + 3] < bboxMin[3] )
|
|
{
|
|
bboxMin[3] = colorBlock[i * 4 + 3];
|
|
}
|
|
if( colorBlock[i * 4 + 0] > bboxMax[0] )
|
|
{
|
|
bboxMax[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] > bboxMax[1] )
|
|
{
|
|
bboxMax[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] > bboxMax[2] )
|
|
{
|
|
bboxMax[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
if( colorBlock[i * 4 + 3] > bboxMax[3] )
|
|
{
|
|
bboxMax[3] = colorBlock[i * 4 + 3];
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < 64; i++ )
|
|
{
|
|
intColorBlock[i] = colorBlock[i];
|
|
}
|
|
|
|
// decrease range for 565 encoding
|
|
bboxMin[0] >>= 3;
|
|
bboxMin[1] >>= 2;
|
|
bboxMin[2] >>= 3;
|
|
bboxMax[0] >>= 3;
|
|
bboxMax[1] >>= 2;
|
|
bboxMax[2] >>= 3;
|
|
|
|
bboxMin[3] = 0;
|
|
bboxMax[3] = 255;
|
|
|
|
// get the minimum distance the end points of the line must be apart along each axis
|
|
for( int i = 0; i < 4; i++ )
|
|
{
|
|
minAxisDist[i] = 0;
|
|
}
|
|
|
|
// expand the bounding box
|
|
const int C565_BBOX_EXPAND = 1;
|
|
const int ALPHA_BBOX_EXPAND = 128;
|
|
|
|
#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;
|
|
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;
|
|
#endif
|
|
|
|
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;
|
|
|
|
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] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for( i1 = bboxMin[1]; i1 <= bboxMax[1]; i1++ )
|
|
{
|
|
for( j1 = bboxMax[1]; j1 >= bboxMin[1]; j1-- )
|
|
{
|
|
if( abs( i1 - j1 ) < minAxisDist[1] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for( i2 = bboxMin[2]; i2 <= bboxMax[2]; i2++ )
|
|
{
|
|
for( j2 = bboxMax[2]; j2 >= bboxMin[2]; j2-- )
|
|
{
|
|
if( abs( i2 - j2 ) < minAxisDist[2] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
unsigned short minColor565 = ( unsigned short )( ( i0 << 11 ) | ( i1 << 5 ) | i2 );
|
|
unsigned short maxColor565 = ( unsigned short )( ( j0 << 11 ) | ( j1 << 5 ) | j2 );
|
|
|
|
if( minColor565 > maxColor565 )
|
|
{
|
|
SwapValues( minColor565, maxColor565 );
|
|
}
|
|
|
|
error = GetSquareNormalsDXT1Error( intColorBlock, maxColor565, minColor565, bestError, tempColorIndices );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
ColorFrom565( minColor565, minColor );
|
|
ColorFrom565( maxColor565, maxColor );
|
|
colorIndices = tempColorIndices;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bestError = MAX_TYPE( int );
|
|
|
|
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] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
tmin[3] = ( byte )j3;
|
|
tmax[3] = ( byte )i3;
|
|
|
|
error = GetSquareNormalsDXT5Error( intColorBlock, tmin, tmax, bestError, tempColorIndices, tempAlphaIndices );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
memcpy( minColor, tmin, 4 );
|
|
memcpy( maxColor, tmax, 4 );
|
|
colorIndices = tempColorIndices;
|
|
memcpy( alphaIndices, tempAlphaIndices, 6 );
|
|
}
|
|
|
|
tmin[3] = ( byte )i3;
|
|
tmax[3] = ( byte )j3;
|
|
|
|
error = GetSquareNormalsDXT5Error( intColorBlock, tmin, tmax, bestError, tempColorIndices, tempAlphaIndices );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
memcpy( minColor, tmin, 4 );
|
|
memcpy( maxColor, tmax, 4 );
|
|
colorIndices = tempColorIndices;
|
|
memcpy( alphaIndices, tempAlphaIndices, 6 );
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
int i, j;
|
|
unsigned int indexes[16];
|
|
byte colors[4][4];
|
|
|
|
ColorFrom565( color0, colors[0] );
|
|
ColorFrom565( color1, colors[1] );
|
|
|
|
if( color0 > color1 )
|
|
{
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
int error = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
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 )
|
|
{
|
|
minDist = dist;
|
|
indexes[i] = j;
|
|
}
|
|
}
|
|
// accumulated error
|
|
error += minDist;
|
|
}
|
|
|
|
result = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
result |= ( indexes[i] << ( unsigned int )( i << 1 ) );
|
|
}
|
|
|
|
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
|
|
{
|
|
int i, j;
|
|
unsigned int indexes[16];
|
|
byte alphas[8];
|
|
|
|
alphas[0] = alpha0;
|
|
alphas[1] = alpha1;
|
|
if( alpha0 > alpha1 )
|
|
{
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
int error = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
unsigned int minDist = MAX_UNSIGNED_TYPE( int );
|
|
byte a = colorBlock[i * 4 + alphaOffset];
|
|
for( j = 0; j < 8; j++ )
|
|
{
|
|
unsigned int dist = AlphaDistance( a, alphas[j] );
|
|
if( dist < minDist )
|
|
{
|
|
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 ) );
|
|
|
|
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
|
|
{
|
|
int i, j;
|
|
unsigned int indexes[16];
|
|
byte colors[4][4];
|
|
|
|
colors[0][0] = color1[0];
|
|
colors[0][1] = color1[1];
|
|
colors[1][0] = color0[0];
|
|
colors[1][1] = color0[1];
|
|
|
|
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;
|
|
|
|
int error = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
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 )
|
|
{
|
|
minDist = dist;
|
|
indexes[i] = j;
|
|
}
|
|
}
|
|
// accumulated error
|
|
error += minDist;
|
|
}
|
|
|
|
result = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
result |= ( indexes[i] << ( unsigned int )( i << 1 ) );
|
|
}
|
|
|
|
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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
unsigned int colorIndices1;
|
|
unsigned int colorIndices2;
|
|
byte col1[4];
|
|
byte col2[4];
|
|
int error1;
|
|
int error2;
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxColorsHQ( block, col1, col2, false );
|
|
|
|
// Write out color data. Try and find minimum error for the two encoding methods.
|
|
unsigned short scol1 = ColorTo565( col1 );
|
|
unsigned short scol2 = ColorTo565( col2 );
|
|
|
|
error1 = FindColorIndices( block, scol1, scol2, colorIndices1 );
|
|
error2 = FindColorIndices( block, scol2, scol1, colorIndices2 );
|
|
|
|
if( error1 < error2 )
|
|
{
|
|
|
|
EmitUShort( scol1 );
|
|
EmitUShort( scol2 );
|
|
EmitUInt( colorIndices1 );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
EmitUShort( scol2 );
|
|
EmitUShort( scol1 );
|
|
EmitUInt( colorIndices2 );
|
|
}
|
|
|
|
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
//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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
byte alphaIndices1[6];
|
|
byte alphaIndices2[6];
|
|
unsigned int colorIndices;
|
|
byte col1[4];
|
|
byte col2[4];
|
|
int error1;
|
|
int error2;
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxColorsHQ( block, col1, col2, true );
|
|
GetMinMaxAlphaHQ( block, 3, col1, col2 );
|
|
|
|
// 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 )
|
|
{
|
|
|
|
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
|
|
{
|
|
|
|
EmitByte( col2[3] );
|
|
EmitByte( col1[3] );
|
|
EmitByte( alphaIndices2[0] );
|
|
EmitByte( alphaIndices2[1] );
|
|
EmitByte( alphaIndices2[2] );
|
|
EmitByte( alphaIndices2[3] );
|
|
EmitByte( alphaIndices2[4] );
|
|
EmitByte( alphaIndices2[5] );
|
|
}
|
|
|
|
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
|
|
NV4XHardwareBugFix( col2, col1 );
|
|
#endif
|
|
|
|
// Write out color data. Always take the path with 4 interpolated values.
|
|
unsigned short scol1 = ColorTo565( col1 );
|
|
unsigned short scol2 = ColorTo565( col2 );
|
|
|
|
EmitUShort( scol1 );
|
|
EmitUShort( scol2 );
|
|
|
|
FindColorIndices( block, scol1, scol2, colorIndices );
|
|
EmitUInt( colorIndices );
|
|
|
|
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
//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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
unsigned int colorIndices;
|
|
byte col1[4];
|
|
byte col2[4];
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxCTX1HQ( block, col1, col2 );
|
|
|
|
EmitByte( col2[0] );
|
|
EmitByte( col2[1] );
|
|
EmitByte( col1[0] );
|
|
EmitByte( col1[1] );
|
|
|
|
FindCTX1Indices( block, col1, col2, colorIndices );
|
|
EmitUInt( colorIndices );
|
|
|
|
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
//idLib::Printf( "\r100%%\n" );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::ScaleYCoCg
|
|
|
|
params: colorBlock - 16 pixel block for which find color indexes
|
|
========================
|
|
*/
|
|
void idDxtEncoder::ScaleYCoCg( byte* colorBlock ) const
|
|
{
|
|
ALIGN16( byte minColor[4] );
|
|
ALIGN16( byte maxColor[4] );
|
|
|
|
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];
|
|
}
|
|
if( colorBlock[i * 4 + 1] < minColor[1] )
|
|
{
|
|
minColor[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 0] > maxColor[0] )
|
|
{
|
|
maxColor[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] > maxColor[1] )
|
|
{
|
|
maxColor[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
const int s0 = 128 / 2 - 1;
|
|
const int s1 = 128 / 4 - 1;
|
|
|
|
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 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
byte alphaIndices1[6];
|
|
byte alphaIndices2[6];
|
|
unsigned int colorIndices;
|
|
byte col1[4];
|
|
byte col2[4];
|
|
int error1;
|
|
int error2;
|
|
|
|
assert( HasConstantValuePer4x4Block( inBuf, width, height, 2 ) );
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
ScaleYCoCg( block );
|
|
|
|
GetMinMaxColorsHQ( block, col1, col2, true );
|
|
GetMinMaxAlphaHQ( block, 3, col1, col2 );
|
|
|
|
// 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 )
|
|
{
|
|
|
|
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
|
|
{
|
|
|
|
EmitByte( col2[3] );
|
|
EmitByte( col1[3] );
|
|
EmitByte( alphaIndices2[0] );
|
|
EmitByte( alphaIndices2[1] );
|
|
EmitByte( alphaIndices2[2] );
|
|
EmitByte( alphaIndices2[3] );
|
|
EmitByte( alphaIndices2[4] );
|
|
EmitByte( alphaIndices2[5] );
|
|
}
|
|
|
|
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
|
|
NV4XHardwareBugFix( col2, col1 );
|
|
#endif
|
|
|
|
// Write out color data. Always take the path with 4 interpolated values.
|
|
unsigned short scol1 = ColorTo565( col1 );
|
|
unsigned short scol2 = ColorTo565( col2 );
|
|
|
|
EmitUShort( scol1 );
|
|
EmitUShort( scol2 );
|
|
|
|
FindColorIndices( block, scol1, scol2, colorIndices );
|
|
EmitUInt( colorIndices );
|
|
|
|
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
//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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
byte alphaIndices1[6];
|
|
byte alphaIndices2[6];
|
|
unsigned int colorIndices;
|
|
byte col1[4];
|
|
byte col2[4];
|
|
int error1;
|
|
int error2;
|
|
|
|
assert( HasConstantValuePer4x4Block( inBuf, width, height, 2 ) );
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxAlphaHQ( block, 3, col1, col2 );
|
|
|
|
// 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 )
|
|
{
|
|
|
|
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
|
|
{
|
|
|
|
EmitByte( col2[3] );
|
|
EmitByte( col1[3] );
|
|
EmitByte( alphaIndices2[0] );
|
|
EmitByte( alphaIndices2[1] );
|
|
EmitByte( alphaIndices2[2] );
|
|
EmitByte( alphaIndices2[3] );
|
|
EmitByte( alphaIndices2[4] );
|
|
EmitByte( alphaIndices2[5] );
|
|
}
|
|
|
|
GetMinMaxCTX1HQ( block, col1, col2 );
|
|
|
|
EmitByte( col2[0] );
|
|
EmitByte( col2[1] );
|
|
EmitByte( col1[0] );
|
|
EmitByte( col1[1] );
|
|
|
|
FindCTX1Indices( block, col1, col2, colorIndices );
|
|
EmitUInt( colorIndices );
|
|
|
|
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
//idLib::Printf( "\r100%%\n" );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::RotateNormalsDXT1
|
|
========================
|
|
*/
|
|
void idDxtEncoder::RotateNormalsDXT1( byte* block ) const
|
|
{
|
|
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 )
|
|
{
|
|
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;
|
|
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 );
|
|
}
|
|
|
|
int error = GetMinMaxColorsHQ( rotatedBlock, col1, col2, true );
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
bestRotation = r;
|
|
}
|
|
}
|
|
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
unsigned int colorIndices;
|
|
byte col1[4];
|
|
byte col2[4];
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
for( int k = 0; k < 16; k++ )
|
|
{
|
|
block[k * 4 + 2] = 0;
|
|
}
|
|
|
|
GetMinMaxColorsHQ( block, col1, col2, true );
|
|
|
|
// Write out color data. Always take the path with 4 interpolated values.
|
|
unsigned short scol1 = ColorTo565( col1 );
|
|
unsigned short scol2 = ColorTo565( col2 );
|
|
|
|
EmitUShort( scol1 );
|
|
EmitUShort( scol2 );
|
|
|
|
FindColorIndices( block, scol1, scol2, colorIndices );
|
|
EmitUInt( colorIndices );
|
|
|
|
//idLib::Printf( "\r%3d%%", ( j * width + i * 4 ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
//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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
unsigned int colorIndices;
|
|
byte col1[4];
|
|
byte col2[4];
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
// clear alpha channel
|
|
for( int k = 0; k < 16; k++ )
|
|
{
|
|
block[k * 4 + 3] = 0;
|
|
}
|
|
|
|
GetMinMaxNormalsDXT1HQ( block, col1, col2, colorIndices, true );
|
|
|
|
// Write out color data. Always take the path with 4 interpolated values.
|
|
unsigned short scol1 = ColorTo565( col1 );
|
|
unsigned short scol2 = ColorTo565( col2 );
|
|
|
|
EmitUShort( scol1 );
|
|
EmitUShort( scol2 );
|
|
EmitUInt( colorIndices );
|
|
|
|
////idLib::Printf( "\r%3d%%", ( j * width + i * 4 ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
////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
|
|
{
|
|
|
|
byte minColor = 255;
|
|
byte maxColor = 0;
|
|
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
if( colorBlock[i * 4 + 1] < minColor )
|
|
{
|
|
minColor = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 1] > maxColor )
|
|
{
|
|
maxColor = colorBlock[i * 4 + 1];
|
|
}
|
|
}
|
|
|
|
int bestBias = 128;
|
|
int bestRange = Max( abs( minColor - bestBias ), abs( maxColor - bestBias ) );
|
|
#if USE_BIAS
|
|
for( int i = 0; i < 32; i++ )
|
|
{
|
|
int bias = ( ( i << 3 ) | ( i >> 2 ) ) - 4;
|
|
int range = Max( abs( minColor - bias ), abs( maxColor - bias ) );
|
|
if( range < bestRange )
|
|
{
|
|
bestRange = range;
|
|
bestBias = bias;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
const int s0 = 128 / 2 - 1;
|
|
const int s1 = 128 / 4 - 1;
|
|
|
|
#if USE_SCALE
|
|
int scale = 1 + ( bestRange <= s0 ) + 2 * ( bestRange <= s1 );
|
|
#else
|
|
int scale = 1;
|
|
#endif
|
|
|
|
if( scale == 1 )
|
|
{
|
|
bestBias = 128;
|
|
}
|
|
else
|
|
{
|
|
c_scaled++;
|
|
if( scale == 2 ) c_scaled2x++;
|
|
if( scale == 4 ) c_scaled4x++;
|
|
if( bestBias != 128 )
|
|
{
|
|
c_differentBias++;
|
|
int r = Max( abs( minColor - 128 ), abs( maxColor - 128 ) );
|
|
int s = 1 + ( r <= s0 ) + 2 * ( r <= s1 );
|
|
if( scale > s )
|
|
{
|
|
c_biasHelped++;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::RotateNormalsDXT5
|
|
========================
|
|
*/
|
|
void idDxtEncoder::RotateNormalsDXT5( byte* block ) const
|
|
{
|
|
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 )
|
|
{
|
|
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;
|
|
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 );
|
|
}
|
|
|
|
#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];
|
|
}
|
|
if( rotatedBlock[j * 4 + 1] > maxColor )
|
|
{
|
|
maxColor = rotatedBlock[j * 4 + 1];
|
|
}
|
|
}
|
|
|
|
const int s0 = 128 / 2 - 1;
|
|
const int s1 = 128 / 4 - 1;
|
|
|
|
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 );
|
|
}
|
|
#endif
|
|
|
|
int errorY = GetMinMaxNormalYHQ( rotatedBlock, col1, col2, true, scale );
|
|
int errorX = GetMinMaxAlphaHQ( rotatedBlock, 3, col1, col2 );
|
|
int error = errorX + errorY;
|
|
if( error < bestError )
|
|
{
|
|
bestError = error;
|
|
bestRotation = r;
|
|
bestScale = scale;
|
|
}
|
|
}
|
|
|
|
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;
|
|
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 );
|
|
|
|
#if USE_SCALE
|
|
block[j * 4 + 1] = byte( ( block[j * 4 + 1] - 128 ) * bestScale + 128 );
|
|
block[j * 4 + 2] = byte( ( bestScale - 1 ) << 3 );
|
|
#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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
byte alphaIndices1[6];
|
|
byte alphaIndices2[6];
|
|
unsigned int colorIndices;
|
|
byte col1[4];
|
|
byte col2[4];
|
|
int error1;
|
|
int error2;
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
// 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;
|
|
}
|
|
|
|
//BiasScaleNormalY( block );
|
|
//RotateNormalsDXT5( block );
|
|
|
|
GetMinMaxNormalYHQ( block, col1, col2, true, 1 );
|
|
GetMinMaxAlphaHQ( block, 3, col1, col2 );
|
|
|
|
// 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 )
|
|
{
|
|
|
|
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
|
|
{
|
|
|
|
EmitByte( col2[3] );
|
|
EmitByte( col1[3] );
|
|
EmitByte( alphaIndices2[0] );
|
|
EmitByte( alphaIndices2[1] );
|
|
EmitByte( alphaIndices2[2] );
|
|
EmitByte( alphaIndices2[3] );
|
|
EmitByte( alphaIndices2[4] );
|
|
EmitByte( alphaIndices2[5] );
|
|
}
|
|
|
|
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
|
|
NV4XHardwareBugFix( col2, col1 );
|
|
#endif
|
|
|
|
// Write out color data. Always take the path with 4 interpolated values.
|
|
unsigned short scol1 = ColorTo565( col1 );
|
|
unsigned short scol2 = ColorTo565( col2 );
|
|
|
|
EmitUShort( scol1 );
|
|
EmitUShort( scol2 );
|
|
|
|
FindColorIndices( block, scol1, scol2, colorIndices );
|
|
EmitUInt( colorIndices );
|
|
|
|
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
//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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
unsigned int colorIndices;
|
|
byte alphaIndices[6];
|
|
byte col1[4];
|
|
byte col2[4];
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
// swizzle components
|
|
for( int k = 0; k < 16; k++ )
|
|
{
|
|
#if 0 // object-space
|
|
block[k * 4 + 3] = block[k * 4 + 2];
|
|
block[k * 4 + 2] = 0;
|
|
#else
|
|
block[k * 4 + 3] = block[k * 4 + 0];
|
|
block[k * 4 + 0] = 0;
|
|
#endif
|
|
}
|
|
|
|
GetMinMaxNormalsDXT5HQFast( block, col1, col2, colorIndices, alphaIndices );
|
|
|
|
EmitByte( col2[3] );
|
|
EmitByte( col1[3] );
|
|
EmitByte( alphaIndices[0] );
|
|
EmitByte( alphaIndices[1] );
|
|
EmitByte( alphaIndices[2] );
|
|
EmitByte( alphaIndices[3] );
|
|
EmitByte( alphaIndices[4] );
|
|
EmitByte( alphaIndices[5] );
|
|
|
|
unsigned short scol1 = ColorTo565( col1 );
|
|
unsigned short scol2 = ColorTo565( col2 );
|
|
|
|
EmitUShort( scol2 );
|
|
EmitUShort( scol1 );
|
|
EmitUInt( colorIndices );
|
|
|
|
////idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
////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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
byte alphaIndices1[6];
|
|
byte alphaIndices2[6];
|
|
byte col1[4];
|
|
byte col2[4];
|
|
int error1;
|
|
int error2;
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
for( int k = 0; k < 2; k++ )
|
|
{
|
|
GetMinMaxAlphaHQ( block, k, col1, col2 );
|
|
|
|
// 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 )
|
|
{
|
|
|
|
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
|
|
{
|
|
|
|
EmitByte( col2[k] );
|
|
EmitByte( col1[k] );
|
|
EmitByte( alphaIndices2[0] );
|
|
EmitByte( alphaIndices2[1] );
|
|
EmitByte( alphaIndices2[2] );
|
|
EmitByte( alphaIndices2[3] );
|
|
EmitByte( alphaIndices2[4] );
|
|
EmitByte( alphaIndices2[5] );
|
|
}
|
|
}
|
|
|
|
//idLib::Printf( "\r%3d%%", ( j * width + i ) * 100 / ( width * height ) );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
|
|
//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
|
|
{
|
|
|
|
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];
|
|
}
|
|
if( colorBlock[i * 4 + 1] < minColor[1] )
|
|
{
|
|
minColor[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] < minColor[2] )
|
|
{
|
|
minColor[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
if( colorBlock[i * 4 + 3] < minColor[3] )
|
|
{
|
|
minColor[3] = colorBlock[i * 4 + 3];
|
|
}
|
|
if( colorBlock[i * 4 + 0] > maxColor[0] )
|
|
{
|
|
maxColor[0] = colorBlock[i * 4 + 0];
|
|
}
|
|
if( colorBlock[i * 4 + 1] > maxColor[1] )
|
|
{
|
|
maxColor[1] = colorBlock[i * 4 + 1];
|
|
}
|
|
if( colorBlock[i * 4 + 2] > maxColor[2] )
|
|
{
|
|
maxColor[2] = colorBlock[i * 4 + 2];
|
|
}
|
|
if( colorBlock[i * 4 + 3] > maxColor[3] )
|
|
{
|
|
maxColor[3] = colorBlock[i * 4 + 3];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::InsetColorsBBox
|
|
========================
|
|
*/
|
|
ID_INLINE void idDxtEncoder::InsetColorsBBox( byte* minColor, byte* maxColor ) const
|
|
{
|
|
byte inset[4];
|
|
|
|
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;
|
|
|
|
minColor[0] = ( minColor[0] + inset[0] <= 255 ) ? minColor[0] + inset[0] : 255;
|
|
minColor[1] = ( minColor[1] + inset[1] <= 255 ) ? minColor[1] + inset[1] : 255;
|
|
minColor[2] = ( minColor[2] + inset[2] <= 255 ) ? minColor[2] + inset[2] : 255;
|
|
minColor[3] = ( minColor[3] + inset[3] <= 255 ) ? minColor[3] + inset[3] : 255;
|
|
|
|
maxColor[0] = ( maxColor[0] >= inset[0] ) ? maxColor[0] - inset[0] : 0;
|
|
maxColor[1] = ( maxColor[1] >= inset[1] ) ? maxColor[1] - inset[1] : 0;
|
|
maxColor[2] = ( maxColor[2] >= inset[2] ) ? maxColor[2] - inset[2] : 0;
|
|
maxColor[3] = ( maxColor[3] >= inset[3] ) ? maxColor[3] - inset[3] : 0;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::SelectColorsDiagonal
|
|
========================
|
|
*/
|
|
void idDxtEncoder::SelectColorsDiagonal( const byte* colorBlock, byte* minColor, byte* maxColor ) const
|
|
{
|
|
|
|
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 );
|
|
|
|
#if 0
|
|
|
|
// 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;
|
|
side0 += ( b0 * b1 );
|
|
side1 += ( b1 * b2 );
|
|
}
|
|
byte mask0 = -( side0 < 0 );
|
|
byte mask1 = -( side1 < 0 );
|
|
|
|
#else
|
|
|
|
// 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;
|
|
side0 += ( b0 ^ b1 );
|
|
side1 += ( b1 ^ b2 );
|
|
}
|
|
byte mask0 = -( side0 > 8 );
|
|
byte mask1 = -( side1 > 8 );
|
|
|
|
#endif
|
|
|
|
byte c0 = minColor[0];
|
|
byte c1 = maxColor[0];
|
|
byte c2 = minColor[2];
|
|
byte c3 = maxColor[2];
|
|
|
|
c0 ^= c1;
|
|
mask0 &= c0;
|
|
c1 ^= mask0;
|
|
c0 ^= c1;
|
|
|
|
c2 ^= c3;
|
|
mask1 &= c2;
|
|
c3 ^= mask1;
|
|
c2 ^= c3;
|
|
|
|
minColor[0] = c0;
|
|
maxColor[0] = c1;
|
|
minColor[2] = c2;
|
|
maxColor[2] = c3;
|
|
|
|
if( ColorTo565( minColor ) > ColorTo565( maxColor ) )
|
|
{
|
|
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 )
|
|
{
|
|
#if 1
|
|
|
|
ALIGN16( uint16 colors[4][4] );
|
|
unsigned int result = 0;
|
|
|
|
colors[0][0] = ( maxColor[0] & C565_5_MASK ) | ( maxColor[0] >> 5 );
|
|
colors[0][1] = ( maxColor[1] & C565_6_MASK ) | ( maxColor[1] >> 6 );
|
|
colors[0][2] = ( maxColor[2] & C565_5_MASK ) | ( maxColor[2] >> 5 );
|
|
colors[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;
|
|
|
|
// uses sum of absolute differences instead of squared distance to find the best match
|
|
for( int i = 15; i >= 0; i-- )
|
|
{
|
|
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];
|
|
|
|
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 );
|
|
|
|
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 );
|
|
|
|
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 );
|
|
|
|
#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;
|
|
|
|
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;
|
|
|
|
int x0 = b1 & b2;
|
|
int x1 = b0 & b3;
|
|
int x2 = b0 & b4;
|
|
|
|
result |= ( x2 | ( ( x0 | x1 ) << 1 ) ) << ( i << 1 );
|
|
#endif
|
|
}
|
|
|
|
EmitUInt( result );
|
|
|
|
#elif 1
|
|
|
|
byte colors[4][4];
|
|
unsigned int indexes[16];
|
|
|
|
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++ )
|
|
{
|
|
int c0, c1, c2, m, d, minDist;
|
|
|
|
c0 = colorBlock[i * 4 + 0];
|
|
c1 = colorBlock[i * 4 + 1];
|
|
c2 = colorBlock[i * 4 + 2];
|
|
|
|
m = colors[0][0] - c0;
|
|
d = m * m;
|
|
m = colors[0][1] - c1;
|
|
d += m * m;
|
|
m = colors[0][2] - c2;
|
|
d += m * m;
|
|
|
|
minDist = d;
|
|
indexes[i] = 0;
|
|
|
|
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 )
|
|
{
|
|
minDist = d;
|
|
indexes[i] = 1;
|
|
}
|
|
|
|
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 )
|
|
{
|
|
minDist = d;
|
|
indexes[i] = 2;
|
|
}
|
|
|
|
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 )
|
|
{
|
|
minDist = d;
|
|
indexes[i] = 3;
|
|
}
|
|
}
|
|
|
|
unsigned int result = 0;
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
result |= ( indexes[i] << ( unsigned int )( i << 1 ) );
|
|
}
|
|
|
|
EmitUInt( result );
|
|
|
|
#else
|
|
|
|
byte colors[4][4];
|
|
unsigned int indexes[16];
|
|
|
|
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 )
|
|
{
|
|
minDist = dist;
|
|
indexes[i] = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int result = 0;
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
result |= ( indexes[i] << ( unsigned int )( i << 1 ) );
|
|
}
|
|
|
|
EmitUInt( result );
|
|
|
|
#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 )
|
|
{
|
|
ALIGN16( uint16 colors[4][4] );
|
|
unsigned int result = 0;
|
|
|
|
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;
|
|
|
|
// uses sum of absolute differences instead of squared distance to find the best match
|
|
for( int i = 15; i >= 0; i-- )
|
|
{
|
|
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];
|
|
|
|
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[0][1] - c1;
|
|
d0 += abs( m );
|
|
m = colors[1][1] - c1;
|
|
d1 += abs( m );
|
|
m = colors[2][1] - c1;
|
|
d2 += abs( m );
|
|
|
|
m = colors[0][2] - c2;
|
|
d0 += abs( m );
|
|
m = colors[1][2] - c2;
|
|
d1 += abs( m );
|
|
m = colors[2][2] - c2;
|
|
d2 += abs( m );
|
|
|
|
unsigned int b0 = d2 > d0;
|
|
unsigned int b1 = d2 > d1;
|
|
unsigned int b2 = d1 > d0;
|
|
unsigned int b3 = c3 < 128;
|
|
|
|
// DG: add some parenthesis to appease (often rightly) warning compiler
|
|
result |= ( ( ( ( b0 & b1 ) | b3 ) << 1 ) | ( ( b2 ^ b1 ) | b3 ) ) << ( i << 1 );
|
|
// DG end
|
|
}
|
|
|
|
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 )
|
|
{
|
|
ALIGN16( uint16 colors[4][2] );
|
|
unsigned int result = 0;
|
|
|
|
colors[0][0] = maxColor[0];
|
|
colors[0][1] = maxColor[1];
|
|
colors[1][0] = minColor[0];
|
|
colors[1][1] = minColor[1];
|
|
|
|
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-- )
|
|
{
|
|
int c0, c1, m, d0, d1, d2, d3;
|
|
|
|
c0 = colorBlock[i * 4 + 0];
|
|
c1 = colorBlock[i * 4 + 1];
|
|
|
|
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 );
|
|
|
|
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 );
|
|
|
|
bool b0 = d0 > d3;
|
|
bool b1 = d1 > d2;
|
|
bool b2 = d0 > d2;
|
|
bool b3 = d1 > d3;
|
|
bool b4 = d2 > d3;
|
|
|
|
int x0 = b1 & b2;
|
|
int x1 = b0 & b3;
|
|
int x2 = b0 & b4;
|
|
|
|
result |= ( x2 | ( ( x0 | x1 ) << 1 ) ) << ( i << 1 );
|
|
}
|
|
|
|
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 )
|
|
{
|
|
|
|
assert( maxAlpha >= minAlpha );
|
|
|
|
const int ALPHA_RANGE = 7;
|
|
|
|
#if 1
|
|
|
|
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 );
|
|
|
|
colorBlock += offset;
|
|
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
byte a = colorBlock[i * 4];
|
|
int b1 = ( a >= ab1 );
|
|
int b2 = ( a >= ab2 );
|
|
int b3 = ( a >= ab3 );
|
|
int b4 = ( a >= ab4 );
|
|
int b5 = ( a >= ab5 );
|
|
int b6 = ( a >= ab6 );
|
|
int b7 = ( a >= ab7 );
|
|
int index = ( 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 ) );
|
|
|
|
#elif 0
|
|
|
|
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;
|
|
|
|
colorBlock += offset;
|
|
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
byte a = colorBlock[i * 4];
|
|
if( a <= bottom )
|
|
{
|
|
indexes[i] = 1;
|
|
}
|
|
else if( a >= top )
|
|
{
|
|
indexes[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
indexes[i] = ( ALPHA_RANGE + 1 ) + ( ( minAlpha - a ) * ALPHA_RANGE - half ) / delta;
|
|
}
|
|
}
|
|
|
|
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 ) );
|
|
|
|
#elif 0
|
|
|
|
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;
|
|
|
|
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;
|
|
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 ) );
|
|
|
|
#else
|
|
|
|
ALIGN16( byte indexes[16] );
|
|
ALIGN16( byte alphas[8] );
|
|
|
|
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;
|
|
|
|
colorBlock += offset;
|
|
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
int minDist = INT_MAX;
|
|
byte a = colorBlock[i * 4];
|
|
for( int j = 0; j < 8; j++ )
|
|
{
|
|
int dist = abs( a - alphas[j] );
|
|
if( dist < minDist )
|
|
{
|
|
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 ) );
|
|
|
|
#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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
ALIGN16( byte minColor[4] );
|
|
ALIGN16( byte maxColor[4] );
|
|
|
|
assert( width >= 4 && ( width & 3 ) == 0 );
|
|
assert( height >= 4 && ( height & 3 ) == 0 );
|
|
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxBBox( block, minColor, maxColor );
|
|
//SelectColorsDiagonal( block, minColor, maxColor );
|
|
InsetColorsBBox( minColor, maxColor );
|
|
|
|
EmitUShort( ColorTo565( maxColor ) );
|
|
EmitUShort( ColorTo565( minColor ) );
|
|
|
|
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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
ALIGN16( byte minColor[4] );
|
|
ALIGN16( byte maxColor[4] );
|
|
|
|
assert( width >= 4 && ( width & 3 ) == 0 );
|
|
assert( height >= 4 && ( height & 3 ) == 0 );
|
|
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxBBox( block, minColor, maxColor );
|
|
byte minAlpha = minColor[3];
|
|
//SelectColorsDiagonal( block, minColor, maxColor );
|
|
InsetColorsBBox( minColor, maxColor );
|
|
|
|
if( minAlpha >= 128 )
|
|
{
|
|
EmitUShort( ColorTo565( maxColor ) );
|
|
EmitUShort( ColorTo565( minColor ) );
|
|
EmitColorIndices( block, minColor, maxColor );
|
|
}
|
|
else
|
|
{
|
|
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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
ALIGN16( byte minColor[4] );
|
|
ALIGN16( byte maxColor[4] );
|
|
|
|
assert( width >= 4 && ( width & 3 ) == 0 );
|
|
assert( height >= 4 && ( height & 3 ) == 0 );
|
|
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxBBox( block, minColor, maxColor );
|
|
//SelectColorsDiagonal( block, minColor, maxColor );
|
|
InsetColorsBBox( minColor, maxColor );
|
|
|
|
EmitByte( maxColor[3] );
|
|
EmitByte( minColor[3] );
|
|
|
|
EmitAlphaIndices( block, 3, minColor[3], maxColor[3] );
|
|
|
|
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
|
|
// the colors are already always guaranteed to be sorted properly
|
|
#endif
|
|
|
|
EmitUShort( ColorTo565( maxColor ) );
|
|
EmitUShort( ColorTo565( minColor ) );
|
|
|
|
EmitColorIndices( block, minColor, maxColor );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::ScaleYCoCg
|
|
========================
|
|
*/
|
|
void idDxtEncoder::ScaleYCoCg( byte* colorBlock, byte* minColor, byte* maxColor ) const
|
|
{
|
|
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;
|
|
|
|
const int s0 = 128 / 2 - 1;
|
|
const int s1 = 128 / 4 - 1;
|
|
|
|
int mask0 = -( m0 <= s0 );
|
|
int mask1 = -( m0 <= s1 );
|
|
int scale = 1 + ( 1 & mask0 ) + ( 2 & mask1 );
|
|
|
|
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 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::InsetYCoCgBBox
|
|
========================
|
|
*/
|
|
ID_INLINE void idDxtEncoder::InsetYCoCgBBox( byte* minColor, byte* maxColor ) const
|
|
{
|
|
|
|
#if 0
|
|
|
|
byte inset[4];
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
minColor[0] = ( minColor[0] & C565_5_MASK ) | ( minColor[0] >> 5 );
|
|
minColor[1] = ( minColor[1] & C565_6_MASK ) | ( minColor[1] >> 6 );
|
|
|
|
maxColor[0] = ( maxColor[0] & C565_5_MASK ) | ( maxColor[0] >> 5 );
|
|
maxColor[1] = ( maxColor[1] & C565_6_MASK ) | ( maxColor[1] >> 6 );
|
|
|
|
#elif 0
|
|
|
|
float inset[4];
|
|
float minf[4];
|
|
float maxf[4];
|
|
|
|
for( int i = 0; i < 4; i++ )
|
|
{
|
|
minf[i] = minColor[i] / 255.0f;
|
|
maxf[i] = maxColor[i] / 255.0f;
|
|
}
|
|
|
|
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++ )
|
|
{
|
|
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 );
|
|
|
|
minColor[0] = ( minColor[0] << 3 ) | ( minColor[0] >> 2 );
|
|
minColor[1] = ( minColor[1] << 2 ) | ( minColor[1] >> 4 );
|
|
|
|
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 );
|
|
|
|
#elif 0
|
|
|
|
int inset[4];
|
|
int mini[4];
|
|
int maxi[4];
|
|
|
|
inset[0] = ( maxColor[0] - minColor[0] );
|
|
inset[1] = ( maxColor[1] - minColor[1] );
|
|
inset[3] = ( maxColor[3] - minColor[3] );
|
|
|
|
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];
|
|
|
|
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;
|
|
|
|
minColor[0] = ( mini[0] << 3 ) | ( mini[0] >> 2 );
|
|
minColor[1] = ( mini[1] << 2 ) | ( mini[1] >> 4 );
|
|
minColor[3] = mini[3];
|
|
|
|
maxColor[0] = ( maxi[0] << 3 ) | ( maxi[0] >> 2 );
|
|
maxColor[1] = ( maxi[1] << 2 ) | ( maxi[1] >> 4 );
|
|
maxColor[3] = maxi[3];
|
|
|
|
#elif 1
|
|
|
|
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 );
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
mini[0] = ( mini[0] >= 0 ) ? mini[0] : 0;
|
|
mini[1] = ( mini[1] >= 0 ) ? mini[1] : 0;
|
|
mini[3] = ( mini[3] >= 0 ) ? mini[3] : 0;
|
|
|
|
maxi[0] = ( maxi[0] <= 255 ) ? maxi[0] : 255;
|
|
maxi[1] = ( maxi[1] <= 255 ) ? maxi[1] : 255;
|
|
maxi[3] = ( maxi[3] <= 255 ) ? maxi[3] : 255;
|
|
|
|
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] );
|
|
|
|
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] );
|
|
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::InsetYCoCgAlpaBBox
|
|
========================
|
|
*/
|
|
ID_INLINE void idDxtEncoder::InsetYCoCgAlpaBBox( byte* minColor, byte* maxColor ) const
|
|
{
|
|
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 );
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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] );
|
|
|
|
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
|
|
{
|
|
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;
|
|
side += ( b0 ^ b1 );
|
|
}
|
|
|
|
byte mask = -( side > 8 );
|
|
|
|
#if defined NVIDIA_7X_HARDWARE_BUG_FIX
|
|
mask &= -( minColor[0] != maxColor[0] );
|
|
#endif
|
|
|
|
byte c0 = minColor[1];
|
|
byte c1 = maxColor[1];
|
|
|
|
c0 ^= c1;
|
|
mask &= c0;
|
|
c1 ^= mask;
|
|
c0 ^= c1;
|
|
|
|
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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
ALIGN16( byte minColor[4] );
|
|
ALIGN16( byte maxColor[4] );
|
|
|
|
//assert( HasConstantValuePer4x4Block( inBuf, width, height, 2 ) );
|
|
|
|
assert( width >= 4 && ( width & 3 ) == 0 );
|
|
assert( height >= 4 && ( height & 3 ) == 0 );
|
|
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxBBox( block, minColor, maxColor );
|
|
ScaleYCoCg( block, minColor, maxColor );
|
|
InsetYCoCgBBox( minColor, maxColor );
|
|
SelectYCoCgDiagonal( block, minColor, maxColor );
|
|
|
|
EmitByte( maxColor[3] );
|
|
EmitByte( minColor[3] );
|
|
|
|
EmitAlphaIndices( block, 3, minColor[3], maxColor[3] );
|
|
|
|
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
|
|
// the colors are already sorted when selecting the diagonal
|
|
#endif
|
|
|
|
EmitUShort( ColorTo565( maxColor ) );
|
|
EmitUShort( ColorTo565( minColor ) );
|
|
|
|
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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
ALIGN16( byte minColor[4] );
|
|
ALIGN16( byte maxColor[4] );
|
|
|
|
assert( width >= 4 && ( width & 3 ) == 0 );
|
|
assert( height >= 4 && ( height & 3 ) == 0 );
|
|
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
GetMinMaxBBox( block, minColor, maxColor );
|
|
InsetYCoCgAlpaBBox( minColor, maxColor );
|
|
SelectColorsDiagonal( block, minColor, maxColor );
|
|
|
|
EmitByte( maxColor[3] );
|
|
EmitByte( minColor[3] );
|
|
|
|
EmitAlphaIndices( block, 3, minColor[3], maxColor[3] );
|
|
|
|
#ifdef NVIDIA_7X_HARDWARE_BUG_FIX
|
|
// the colors are already sorted when selecting the diagonal
|
|
#endif
|
|
|
|
EmitUShort( ColorTo565( maxColor ) );
|
|
EmitUShort( ColorTo565( minColor ) );
|
|
|
|
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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
ALIGN16( byte minColor[4] );
|
|
ALIGN16( byte maxColor[4] );
|
|
|
|
assert( HasConstantValuePer4x4Block( inBuf, width, height, 2 ) );
|
|
|
|
assert( width >= 4 && ( width & 3 ) == 0 );
|
|
assert( height >= 4 && ( height & 3 ) == 0 );
|
|
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxBBox( block, minColor, maxColor );
|
|
SelectYCoCgDiagonal( block, minColor, maxColor );
|
|
InsetColorsBBox( minColor, maxColor );
|
|
|
|
EmitByte( maxColor[3] );
|
|
EmitByte( minColor[3] );
|
|
|
|
EmitAlphaIndices( block, 3, minColor[3], maxColor[3] );
|
|
|
|
EmitByte( maxColor[0] );
|
|
EmitByte( maxColor[1] );
|
|
EmitByte( minColor[0] );
|
|
EmitByte( minColor[1] );
|
|
|
|
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 )
|
|
{
|
|
|
|
assert( maxGreen >= minGreen );
|
|
|
|
const int COLOR_RANGE = 3;
|
|
|
|
#if 1
|
|
|
|
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 );
|
|
|
|
unsigned int result = 0;
|
|
|
|
block += offset;
|
|
|
|
for( int i = 15; i >= 0; i-- )
|
|
{
|
|
result <<= 2;
|
|
byte y = block[i * 4];
|
|
int b1 = ( y >= yb1 );
|
|
int b2 = ( y >= yb2 );
|
|
int b3 = ( y >= yb3 );
|
|
int index = ( 4 - b1 - b2 - b3 ) & 3;
|
|
index ^= ( 2 > index );
|
|
result |= index;
|
|
}
|
|
|
|
EmitUInt( result );
|
|
|
|
#else
|
|
|
|
byte green[4];
|
|
|
|
green[0] = maxGreen;
|
|
green[1] = minGreen;
|
|
green[2] = ( 2 * green[0] + 1 * green[1] ) / 3;
|
|
green[3] = ( 1 * green[0] + 2 * green[1] ) / 3;
|
|
|
|
unsigned int result = 0;
|
|
|
|
block += offset;
|
|
|
|
for( int i = 15; i >= 0; i-- )
|
|
{
|
|
result <<= 2;
|
|
byte y = block[i * 4];
|
|
int minDist = INT_MAX;
|
|
int index;
|
|
for( int j = 0; j < 4; j++ )
|
|
{
|
|
int dist = abs( y - green[j] );
|
|
if( dist < minDist )
|
|
{
|
|
minDist = dist;
|
|
index = j;
|
|
}
|
|
}
|
|
result |= index;
|
|
}
|
|
|
|
EmitUInt( result );
|
|
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::InsetNormalsBBoxDXT5
|
|
========================
|
|
*/
|
|
void idDxtEncoder::InsetNormalsBBoxDXT5( byte* minNormal, byte* maxNormal ) const
|
|
{
|
|
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 );
|
|
|
|
mini[3] = ( ( minNormal[3] << INSET_ALPHA_SHIFT ) + inset[3] ) >> INSET_ALPHA_SHIFT;
|
|
mini[1] = ( ( minNormal[1] << INSET_COLOR_SHIFT ) + inset[1] ) >> INSET_COLOR_SHIFT;
|
|
|
|
maxi[3] = ( ( maxNormal[3] << INSET_ALPHA_SHIFT ) - inset[3] ) >> INSET_ALPHA_SHIFT;
|
|
maxi[1] = ( ( maxNormal[1] << INSET_COLOR_SHIFT ) - inset[1] ) >> INSET_COLOR_SHIFT;
|
|
|
|
mini[3] = ( mini[3] >= 0 ) ? mini[3] : 0;
|
|
mini[1] = ( mini[1] >= 0 ) ? mini[1] : 0;
|
|
|
|
maxi[3] = ( maxi[3] <= 255 ) ? maxi[3] : 255;
|
|
maxi[1] = ( maxi[1] <= 255 ) ? maxi[1] : 255;
|
|
|
|
minNormal[3] = byte( mini[3] );
|
|
minNormal[1] = byte( ( mini[1] & C565_6_MASK ) | ( mini[1] >> 6 ) );
|
|
|
|
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];
|
|
}
|
|
|
|
/*
|
|
========================
|
|
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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
ALIGN16( byte normal1[4] );
|
|
ALIGN16( byte normal2[4] );
|
|
|
|
assert( width >= 4 && ( width & 3 ) == 0 );
|
|
assert( height >= 4 && ( height & 3 ) == 0 );
|
|
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxBBox( block, normal1, normal2 );
|
|
InsetNormalsBBoxDXT5( normal1, normal2 );
|
|
|
|
// Write out Nx into alpha channel.
|
|
EmitByte( normal2[3] );
|
|
EmitByte( normal1[3] );
|
|
EmitAlphaIndices( block, 3, normal1[3], normal2[3] );
|
|
|
|
// 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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
ALIGN16( byte min[4] );
|
|
ALIGN16( byte max[4] );
|
|
|
|
assert( width >= 4 && ( width & 3 ) == 0 );
|
|
assert( height >= 4 && ( height & 3 ) == 0 );
|
|
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxBBox( block, min, max );
|
|
InsetNormalsBBox3Dc( min, max );
|
|
|
|
// 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 )
|
|
{
|
|
ALIGN16( byte block[64] );
|
|
ALIGN16( byte normal1[4] );
|
|
ALIGN16( byte normal2[4] );
|
|
|
|
assert( width >= 4 && ( width & 3 ) == 0 );
|
|
assert( height >= 4 && ( height & 3 ) == 0 );
|
|
|
|
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 )
|
|
{
|
|
|
|
ExtractBlock( inBuf + i * 4, width, block );
|
|
|
|
GetMinMaxBBox( block, normal1, normal2 );
|
|
InsetNormalsBBox3Dc( normal1, normal2 );
|
|
|
|
// Write out Nx as an alpha channel.
|
|
EmitByte( normal2[0] );
|
|
EmitByte( normal1[0] );
|
|
EmitAlphaIndices( block, 0, normal1[0], normal2[0] );
|
|
|
|
// 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 )
|
|
{
|
|
int i;
|
|
unsigned int indices;
|
|
byte alphas[8];
|
|
|
|
if( inBuf[0] <= inBuf[1] )
|
|
{
|
|
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
|
|
{
|
|
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++ )
|
|
{
|
|
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++ )
|
|
{
|
|
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 )
|
|
{
|
|
|
|
const int COLOR_RANGE = 3;
|
|
|
|
byte maskedMin, maskedMax, mid, yb1, yb2, yb3;
|
|
|
|
maskedMax = max & C565_6_MASK;
|
|
maskedMin = min & C565_6_MASK;
|
|
mid = ( maskedMax - maskedMin ) / ( 2 * COLOR_RANGE );
|
|
|
|
yb1 = maskedMax - mid;
|
|
yb2 = ( 2 * maskedMax + 1 * maskedMin ) / COLOR_RANGE - mid;
|
|
yb3 = ( 1 * maskedMax + 2 * maskedMin ) / COLOR_RANGE - mid;
|
|
|
|
unsigned int result = 0;
|
|
|
|
for( int i = 15; i >= 0; i-- )
|
|
{
|
|
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;
|
|
|
|
outBuf[0] = byte( ( smax >> 0 ) & 0xFF );
|
|
outBuf[1] = byte( ( smax >> 8 ) & 0xFF );
|
|
outBuf[2] = byte( ( smin >> 0 ) & 0xFF );
|
|
outBuf[3] = byte( ( smin >> 8 ) & 0xFF );
|
|
|
|
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 )
|
|
{
|
|
ALIGN16( byte values[16] );
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
for( int j = 0; j < height; j += 4 )
|
|
{
|
|
for( int i = 0; i < width; i += 4, inBuf += 16, outBuf += 16 )
|
|
{
|
|
|
|
// decode normal Y stored as a DXT5 alpha channel
|
|
DecodeDXNAlphaValues( inBuf + 0, values );
|
|
|
|
// copy normal X
|
|
memcpy( outBuf + 0, inBuf + 8, 8 );
|
|
|
|
// get the min/max Y
|
|
byte minNormalY = 255;
|
|
byte maxNormalY = 0;
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
if( values[i] < minNormalY )
|
|
{
|
|
minNormalY = values[i];
|
|
}
|
|
if( values[i] > maxNormalY )
|
|
{
|
|
maxNormalY = values[i];
|
|
}
|
|
}
|
|
|
|
// 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 )
|
|
{
|
|
int i;
|
|
unsigned int indexes;
|
|
unsigned short normal0, normal1;
|
|
byte normalsY[4];
|
|
|
|
normal0 = inBuf[0] | ( inBuf[1] << 8 );
|
|
normal1 = inBuf[2] | ( inBuf[3] << 8 );
|
|
|
|
assert( normal0 >= normal1 );
|
|
|
|
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++ )
|
|
{
|
|
values[i] = normalsY[indexes & 3];
|
|
indexes >>= 2;
|
|
}
|
|
|
|
max = normalsY[0];
|
|
min = normalsY[1];
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idDxtEncoder::EncodeDXNAlphaValues
|
|
========================
|
|
*/
|
|
void idDxtEncoder::EncodeDXNAlphaValues( byte* outBuf, const byte min, const byte max, const byte* values )
|
|
{
|
|
int i;
|
|
byte alphas[8];
|
|
int j;
|
|
unsigned int indexes[16];
|
|
|
|
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;
|
|
|
|
int error = 0;
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
int minDist = MAX_TYPE( int );
|
|
byte a = values[i];
|
|
for( j = 0; j < 8; j++ )
|
|
{
|
|
int dist = AlphaDistance( a, alphas[j] );
|
|
if( dist < minDist )
|
|
{
|
|
minDist = dist;
|
|
indexes[i] = j;
|
|
}
|
|
}
|
|
error += minDist;
|
|
}
|
|
|
|
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 ) );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
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 )
|
|
{
|
|
ALIGN16( byte values[16] );
|
|
byte minNormalY, maxNormalY;
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
for( int j = 0; j < height; j += 4 )
|
|
{
|
|
for( int i = 0; i < width; i += 4, inBuf += 16, outBuf += 16 )
|
|
{
|
|
|
|
// decode normal Y stored as a DXT5 alpha channel
|
|
DecodeNormalYValues( inBuf + 8, minNormalY, maxNormalY, values );
|
|
|
|
memcpy( outBuf + 8, inBuf + 0, 8 );
|
|
|
|
// 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 )
|
|
{
|
|
ALIGN16( byte values[16] );
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
this->outData = outBuf;
|
|
|
|
if( width > 4 && ( width & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( height > 4 && ( height & 3 ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( width < 4 || height < 4 )
|
|
{
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
for( int j = 0; j < height; j += 4 )
|
|
{
|
|
for( int i = 0; i < width; i += 4, inBuf += 8, outBuf += 8 )
|
|
{
|
|
|
|
// decode single channel stored as a DXT5 alpha channel
|
|
DecodeDXNAlphaValues( inBuf + 0, values );
|
|
|
|
// get the min/max
|
|
byte min = 255;
|
|
byte max = 0;
|
|
for( int i = 0; i < 16; i++ )
|
|
{
|
|
if( values[i] < min )
|
|
{
|
|
min = values[i];
|
|
}
|
|
if( values[i] > max )
|
|
{
|
|
max = values[i];
|
|
}
|
|
}
|
|
|
|
// encode single channel into DXT1
|
|
EncodeNormalRGBIndices( outBuf + 0, min, max, values );
|
|
}
|
|
outData += dstPadding;
|
|
inBuf += srcPadding;
|
|
}
|
|
}
|