mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-11-28 06:41:59 +00:00
4691 lines
130 KiB
C++
4691 lines
130 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 ) {
|
|
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;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
NormalDistanceDXT5
|
|
========================
|
|
*/
|
|
int NormalDistanceDXT5( const int *vector, const int *normalized ) {
|
|
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;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
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;
|
|
|
|
result |= ( ( ( b0 & b1 | b3 ) << 1 ) | ( b2 ^ b1 | b3 ) ) << ( i << 1 );
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|