squish: Update to upstream 1.14

Sources are untouched, tarball from https://sourceforge.net/projects/libsquish

(cherry picked from commit 249836e530)
This commit is contained in:
Rémi Verschelde 2016-10-13 21:52:16 +02:00
parent 1022705707
commit 8263fca121
25 changed files with 3912 additions and 3643 deletions

View File

@ -150,9 +150,8 @@ Files extracted from upstream source:
## squish ## squish
- Upstream: https://code.google.com/archive/p/libsquish - Upstream: https://sourceforge.net/projects/libsquish
and patches from https://github.com/Cavewhere/squish - Version: 1.14
- Version: 1.11
- License: MIT - License: MIT
Files extracted from upstream source: Files extracted from upstream source:

View File

@ -1,29 +1,30 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#include "alpha.h" #include "alpha.h"
#include <climits> #include <climits>
#include <algorithm> #include <algorithm>
@ -31,319 +32,319 @@ namespace squish {
static int FloatToInt( float a, int limit ) static int FloatToInt( float a, int limit )
{ {
// use ANSI round-to-zero behaviour to get round-to-nearest // use ANSI round-to-zero behaviour to get round-to-nearest
int i = ( int )( a + 0.5f ); int i = ( int )( a + 0.5f );
// clamp to the limit // clamp to the limit
if( i < 0 ) if( i < 0 )
i = 0; i = 0;
else if( i > limit ) else if( i > limit )
i = limit; i = limit;
// done // done
return i; return i;
} }
void CompressAlphaDxt3( u8 const* rgba, int mask, void* block ) void CompressAlphaDxt3( u8 const* rgba, int mask, void* block )
{ {
u8* bytes = reinterpret_cast< u8* >( block ); u8* bytes = reinterpret_cast< u8* >( block );
// quantise and pack the alpha values pairwise
for( int i = 0; i < 8; ++i )
{
// quantise down to 4 bits
float alpha1 = ( float )rgba[8*i + 3] * ( 15.0f/255.0f );
float alpha2 = ( float )rgba[8*i + 7] * ( 15.0f/255.0f );
int quant1 = FloatToInt( alpha1, 15 );
int quant2 = FloatToInt( alpha2, 15 );
// set alpha to zero where masked
int bit1 = 1 << ( 2*i );
int bit2 = 1 << ( 2*i + 1 );
if( ( mask & bit1 ) == 0 )
quant1 = 0;
if( ( mask & bit2 ) == 0 )
quant2 = 0;
// pack into the byte // quantise and pack the alpha values pairwise
bytes[i] = ( u8 )( quant1 | ( quant2 << 4 ) ); for( int i = 0; i < 8; ++i )
} {
// quantise down to 4 bits
float alpha1 = ( float )rgba[8*i + 3] * ( 15.0f/255.0f );
float alpha2 = ( float )rgba[8*i + 7] * ( 15.0f/255.0f );
int quant1 = FloatToInt( alpha1, 15 );
int quant2 = FloatToInt( alpha2, 15 );
// set alpha to zero where masked
int bit1 = 1 << ( 2*i );
int bit2 = 1 << ( 2*i + 1 );
if( ( mask & bit1 ) == 0 )
quant1 = 0;
if( ( mask & bit2 ) == 0 )
quant2 = 0;
// pack into the byte
bytes[i] = ( u8 )( quant1 | ( quant2 << 4 ) );
}
} }
void DecompressAlphaDxt3( u8* rgba, void const* block ) void DecompressAlphaDxt3( u8* rgba, void const* block )
{ {
u8 const* bytes = reinterpret_cast< u8 const* >( block ); u8 const* bytes = reinterpret_cast< u8 const* >( block );
// unpack the alpha values pairwise
for( int i = 0; i < 8; ++i )
{
// quantise down to 4 bits
u8 quant = bytes[i];
// unpack the values
u8 lo = quant & 0x0f;
u8 hi = quant & 0xf0;
// convert back up to bytes // unpack the alpha values pairwise
rgba[8*i + 3] = lo | ( lo << 4 ); for( int i = 0; i < 8; ++i )
rgba[8*i + 7] = hi | ( hi >> 4 ); {
} // quantise down to 4 bits
u8 quant = bytes[i];
// unpack the values
u8 lo = quant & 0x0f;
u8 hi = quant & 0xf0;
// convert back up to bytes
rgba[8*i + 3] = lo | ( lo << 4 );
rgba[8*i + 7] = hi | ( hi >> 4 );
}
} }
static void FixRange( int& min, int& max, int steps ) static void FixRange( int& min, int& max, int steps )
{ {
if( max - min < steps ) if( max - min < steps )
max = std::min( min + steps, 255 ); max = std::min( min + steps, 255 );
if( max - min < steps ) if( max - min < steps )
min = std::max( 0, max - steps ); min = std::max( 0, max - steps );
} }
static int FitCodes( u8 const* rgba, int mask, u8 const* codes, u8* indices ) static int FitCodes( u8 const* rgba, int mask, u8 const* codes, u8* indices )
{ {
// fit each alpha value to the codebook // fit each alpha value to the codebook
int err = 0; int err = 0;
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
{ {
// check this pixel is valid // check this pixel is valid
int bit = 1 << i; int bit = 1 << i;
if( ( mask & bit ) == 0 ) if( ( mask & bit ) == 0 )
{ {
// use the first code // use the first code
indices[i] = 0; indices[i] = 0;
continue; continue;
} }
// find the least error and corresponding index // find the least error and corresponding index
int value = rgba[4*i + 3]; int value = rgba[4*i + 3];
int least = INT_MAX; int least = INT_MAX;
int index = 0; int index = 0;
for( int j = 0; j < 8; ++j ) for( int j = 0; j < 8; ++j )
{ {
// get the squared error from this code // get the squared error from this code
int dist = ( int )value - ( int )codes[j]; int dist = ( int )value - ( int )codes[j];
dist *= dist; dist *= dist;
// compare with the best so far // compare with the best so far
if( dist < least ) if( dist < least )
{ {
least = dist; least = dist;
index = j; index = j;
} }
} }
// save this index and accumulate the error // save this index and accumulate the error
indices[i] = ( u8 )index; indices[i] = ( u8 )index;
err += least; err += least;
} }
// return the total error // return the total error
return err; return err;
} }
static void WriteAlphaBlock( int alpha0, int alpha1, u8 const* indices, void* block ) static void WriteAlphaBlock( int alpha0, int alpha1, u8 const* indices, void* block )
{ {
u8* bytes = reinterpret_cast< u8* >( block ); u8* bytes = reinterpret_cast< u8* >( block );
// write the first two bytes // write the first two bytes
bytes[0] = ( u8 )alpha0; bytes[0] = ( u8 )alpha0;
bytes[1] = ( u8 )alpha1; bytes[1] = ( u8 )alpha1;
// pack the indices with 3 bits each // pack the indices with 3 bits each
u8* dest = bytes + 2; u8* dest = bytes + 2;
u8 const* src = indices; u8 const* src = indices;
for( int i = 0; i < 2; ++i ) for( int i = 0; i < 2; ++i )
{ {
// pack 8 3-bit values // pack 8 3-bit values
int value = 0; int value = 0;
for( int j = 0; j < 8; ++j ) for( int j = 0; j < 8; ++j )
{ {
int index = *src++; int index = *src++;
value |= ( index << 3*j ); value |= ( index << 3*j );
} }
// store in 3 bytes // store in 3 bytes
for( int j = 0; j < 3; ++j ) for( int j = 0; j < 3; ++j )
{ {
int byte = ( value >> 8*j ) & 0xff; int byte = ( value >> 8*j ) & 0xff;
*dest++ = ( u8 )byte; *dest++ = ( u8 )byte;
} }
} }
} }
static void WriteAlphaBlock5( int alpha0, int alpha1, u8 const* indices, void* block ) static void WriteAlphaBlock5( int alpha0, int alpha1, u8 const* indices, void* block )
{ {
// check the relative values of the endpoints // check the relative values of the endpoints
if( alpha0 > alpha1 ) if( alpha0 > alpha1 )
{ {
// swap the indices // swap the indices
u8 swapped[16]; u8 swapped[16];
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
{ {
u8 index = indices[i]; u8 index = indices[i];
if( index == 0 ) if( index == 0 )
swapped[i] = 1; swapped[i] = 1;
else if( index == 1 ) else if( index == 1 )
swapped[i] = 0; swapped[i] = 0;
else if( index <= 5 ) else if( index <= 5 )
swapped[i] = 7 - index; swapped[i] = 7 - index;
else else
swapped[i] = index; swapped[i] = index;
} }
// write the block // write the block
WriteAlphaBlock( alpha1, alpha0, swapped, block ); WriteAlphaBlock( alpha1, alpha0, swapped, block );
} }
else else
{ {
// write the block // write the block
WriteAlphaBlock( alpha0, alpha1, indices, block ); WriteAlphaBlock( alpha0, alpha1, indices, block );
} }
} }
static void WriteAlphaBlock7( int alpha0, int alpha1, u8 const* indices, void* block ) static void WriteAlphaBlock7( int alpha0, int alpha1, u8 const* indices, void* block )
{ {
// check the relative values of the endpoints // check the relative values of the endpoints
if( alpha0 < alpha1 ) if( alpha0 < alpha1 )
{ {
// swap the indices // swap the indices
u8 swapped[16]; u8 swapped[16];
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
{ {
u8 index = indices[i]; u8 index = indices[i];
if( index == 0 ) if( index == 0 )
swapped[i] = 1; swapped[i] = 1;
else if( index == 1 ) else if( index == 1 )
swapped[i] = 0; swapped[i] = 0;
else else
swapped[i] = 9 - index; swapped[i] = 9 - index;
} }
// write the block // write the block
WriteAlphaBlock( alpha1, alpha0, swapped, block ); WriteAlphaBlock( alpha1, alpha0, swapped, block );
} }
else else
{ {
// write the block // write the block
WriteAlphaBlock( alpha0, alpha1, indices, block ); WriteAlphaBlock( alpha0, alpha1, indices, block );
} }
} }
void CompressAlphaDxt5( u8 const* rgba, int mask, void* block ) void CompressAlphaDxt5( u8 const* rgba, int mask, void* block )
{ {
// get the range for 5-alpha and 7-alpha interpolation // get the range for 5-alpha and 7-alpha interpolation
int min5 = 255; int min5 = 255;
int max5 = 0; int max5 = 0;
int min7 = 255; int min7 = 255;
int max7 = 0; int max7 = 0;
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
{ {
// check this pixel is valid // check this pixel is valid
int bit = 1 << i; int bit = 1 << i;
if( ( mask & bit ) == 0 ) if( ( mask & bit ) == 0 )
continue; continue;
// incorporate into the min/max // incorporate into the min/max
int value = rgba[4*i + 3]; int value = rgba[4*i + 3];
if( value < min7 ) if( value < min7 )
min7 = value; min7 = value;
if( value > max7 ) if( value > max7 )
max7 = value; max7 = value;
if( value != 0 && value < min5 ) if( value != 0 && value < min5 )
min5 = value; min5 = value;
if( value != 255 && value > max5 ) if( value != 255 && value > max5 )
max5 = value; max5 = value;
} }
// handle the case that no valid range was found // handle the case that no valid range was found
if( min5 > max5 ) if( min5 > max5 )
min5 = max5; min5 = max5;
if( min7 > max7 ) if( min7 > max7 )
min7 = max7; min7 = max7;
// fix the range to be the minimum in each case // fix the range to be the minimum in each case
FixRange( min5, max5, 5 ); FixRange( min5, max5, 5 );
FixRange( min7, max7, 7 ); FixRange( min7, max7, 7 );
// set up the 5-alpha code book // set up the 5-alpha code book
u8 codes5[8]; u8 codes5[8];
codes5[0] = ( u8 )min5; codes5[0] = ( u8 )min5;
codes5[1] = ( u8 )max5; codes5[1] = ( u8 )max5;
for( int i = 1; i < 5; ++i ) for( int i = 1; i < 5; ++i )
codes5[1 + i] = ( u8 )( ( ( 5 - i )*min5 + i*max5 )/5 ); codes5[1 + i] = ( u8 )( ( ( 5 - i )*min5 + i*max5 )/5 );
codes5[6] = 0; codes5[6] = 0;
codes5[7] = 255; codes5[7] = 255;
// set up the 7-alpha code book // set up the 7-alpha code book
u8 codes7[8]; u8 codes7[8];
codes7[0] = ( u8 )min7; codes7[0] = ( u8 )min7;
codes7[1] = ( u8 )max7; codes7[1] = ( u8 )max7;
for( int i = 1; i < 7; ++i ) for( int i = 1; i < 7; ++i )
codes7[1 + i] = ( u8 )( ( ( 7 - i )*min7 + i*max7 )/7 ); codes7[1 + i] = ( u8 )( ( ( 7 - i )*min7 + i*max7 )/7 );
// fit the data to both code books // fit the data to both code books
u8 indices5[16]; u8 indices5[16];
u8 indices7[16]; u8 indices7[16];
int err5 = FitCodes( rgba, mask, codes5, indices5 ); int err5 = FitCodes( rgba, mask, codes5, indices5 );
int err7 = FitCodes( rgba, mask, codes7, indices7 ); int err7 = FitCodes( rgba, mask, codes7, indices7 );
// save the block with least error // save the block with least error
if( err5 <= err7 ) if( err5 <= err7 )
WriteAlphaBlock5( min5, max5, indices5, block ); WriteAlphaBlock5( min5, max5, indices5, block );
else else
WriteAlphaBlock7( min7, max7, indices7, block ); WriteAlphaBlock7( min7, max7, indices7, block );
} }
void DecompressAlphaDxt5( u8* rgba, void const* block ) void DecompressAlphaDxt5( u8* rgba, void const* block )
{ {
// get the two alpha values // get the two alpha values
u8 const* bytes = reinterpret_cast< u8 const* >( block ); u8 const* bytes = reinterpret_cast< u8 const* >( block );
int alpha0 = bytes[0]; int alpha0 = bytes[0];
int alpha1 = bytes[1]; int alpha1 = bytes[1];
// compare the values to build the codebook // compare the values to build the codebook
u8 codes[8]; u8 codes[8];
codes[0] = ( u8 )alpha0; codes[0] = ( u8 )alpha0;
codes[1] = ( u8 )alpha1; codes[1] = ( u8 )alpha1;
if( alpha0 <= alpha1 ) if( alpha0 <= alpha1 )
{ {
// use 5-alpha codebook // use 5-alpha codebook
for( int i = 1; i < 5; ++i ) for( int i = 1; i < 5; ++i )
codes[1 + i] = ( u8 )( ( ( 5 - i )*alpha0 + i*alpha1 )/5 ); codes[1 + i] = ( u8 )( ( ( 5 - i )*alpha0 + i*alpha1 )/5 );
codes[6] = 0; codes[6] = 0;
codes[7] = 255; codes[7] = 255;
} }
else else
{ {
// use 7-alpha codebook // use 7-alpha codebook
for( int i = 1; i < 7; ++i ) for( int i = 1; i < 7; ++i )
codes[1 + i] = ( u8 )( ( ( 7 - i )*alpha0 + i*alpha1 )/7 ); codes[1 + i] = ( u8 )( ( ( 7 - i )*alpha0 + i*alpha1 )/7 );
} }
// decode the indices // decode the indices
u8 indices[16]; u8 indices[16];
u8 const* src = bytes + 2; u8 const* src = bytes + 2;
u8* dest = indices; u8* dest = indices;
for( int i = 0; i < 2; ++i ) for( int i = 0; i < 2; ++i )
{ {
// grab 3 bytes // grab 3 bytes
int value = 0; int value = 0;
for( int j = 0; j < 3; ++j ) for( int j = 0; j < 3; ++j )
{ {
int byte = *src++; int byte = *src++;
value |= ( byte << 8*j ); value |= ( byte << 8*j );
} }
// unpack 8 3-bit values from it // unpack 8 3-bit values from it
for( int j = 0; j < 8; ++j ) for( int j = 0; j < 8; ++j )
{ {
int index = ( value >> 3*j ) & 0x7; int index = ( value >> 3*j ) & 0x7;
*dest++ = ( u8 )index; *dest++ = ( u8 )index;
} }
} }
// write out the indexed codebook values // write out the indexed codebook values
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
rgba[4*i + 3] = codes[indices[i]]; rgba[4*i + 3] = codes[indices[i]];
} }
} // namespace squish } // namespace squish

View File

@ -1,32 +1,32 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_ALPHA_H #ifndef SQUISH_ALPHA_H
#define SQUISH_ALPHA_H #define SQUISH_ALPHA_H
#include <squish.h> #include "squish.h"
namespace squish { namespace squish {

View File

@ -1,29 +1,29 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Copyright (c) 2007 Ignacio Castano icastano@nvidia.com Copyright (c) 2007 Ignacio Castano icastano@nvidia.com
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#include "clusterfit.h" #include "clusterfit.h"
#include "colourset.h" #include "colourset.h"
#include "colourblock.h" #include "colourblock.h"
@ -31,363 +31,362 @@
namespace squish { namespace squish {
ClusterFit::ClusterFit( ColourSet const* colours, int flags ) ClusterFit::ClusterFit( ColourSet const* colours, int flags, float* metric )
: ColourFit( colours, flags ) : ColourFit( colours, flags )
{ {
// set the iteration count // set the iteration count
m_iterationCount = ( m_flags & kColourIterativeClusterFit ) ? kMaxIterations : 1; m_iterationCount = ( m_flags & kColourIterativeClusterFit ) ? kMaxIterations : 1;
// initialise the best error // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)
m_besterror = VEC4_CONST( FLT_MAX ); if( metric )
m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f );
else
m_metric = VEC4_CONST( 1.0f );
// initialise the metric // initialise the best error
bool perceptual = ( ( m_flags & kColourMetricPerceptual ) != 0 ); m_besterror = VEC4_CONST( FLT_MAX );
if( perceptual )
m_metric = Vec4( 0.2126f, 0.7152f, 0.0722f, 0.0f );
else
m_metric = VEC4_CONST( 1.0f );
// cache some values // cache some values
int const count = m_colours->GetCount(); int const count = m_colours->GetCount();
Vec3 const* values = m_colours->GetPoints(); Vec3 const* values = m_colours->GetPoints();
// get the covariance matrix // get the covariance matrix
Sym3x3 covariance = ComputeWeightedCovariance( count, values, m_colours->GetWeights() ); Sym3x3 covariance = ComputeWeightedCovariance( count, values, m_colours->GetWeights() );
// compute the principle component // compute the principle component
m_principle = ComputePrincipleComponent( covariance ); m_principle = ComputePrincipleComponent( covariance );
} }
bool ClusterFit::ConstructOrdering( Vec3 const& axis, int iteration ) bool ClusterFit::ConstructOrdering( Vec3 const& axis, int iteration )
{ {
// cache some values // cache some values
int const count = m_colours->GetCount(); int const count = m_colours->GetCount();
Vec3 const* values = m_colours->GetPoints(); Vec3 const* values = m_colours->GetPoints();
// build the list of dot products // build the list of dot products
float dps[16]; float dps[16];
u8* order = ( u8* )m_order + 16*iteration; u8* order = ( u8* )m_order + 16*iteration;
for( int i = 0; i < count; ++i ) for( int i = 0; i < count; ++i )
{ {
dps[i] = Dot( values[i], axis ); dps[i] = Dot( values[i], axis );
order[i] = ( u8 )i; order[i] = ( u8 )i;
} }
// stable sort using them // stable sort using them
for( int i = 0; i < count; ++i ) for( int i = 0; i < count; ++i )
{ {
for( int j = i; j > 0 && dps[j] < dps[j - 1]; --j ) for( int j = i; j > 0 && dps[j] < dps[j - 1]; --j )
{ {
std::swap( dps[j], dps[j - 1] ); std::swap( dps[j], dps[j - 1] );
std::swap( order[j], order[j - 1] ); std::swap( order[j], order[j - 1] );
} }
} }
// check this ordering is unique // check this ordering is unique
for( int it = 0; it < iteration; ++it ) for( int it = 0; it < iteration; ++it )
{ {
u8 const* prev = ( u8* )m_order + 16*it; u8 const* prev = ( u8* )m_order + 16*it;
bool same = true; bool same = true;
for( int i = 0; i < count; ++i ) for( int i = 0; i < count; ++i )
{ {
if( order[i] != prev[i] ) if( order[i] != prev[i] )
{ {
same = false; same = false;
break; break;
} }
} }
if( same ) if( same )
return false; return false;
} }
// copy the ordering and weight all the points // copy the ordering and weight all the points
Vec3 const* unweighted = m_colours->GetPoints(); Vec3 const* unweighted = m_colours->GetPoints();
float const* weights = m_colours->GetWeights(); float const* weights = m_colours->GetWeights();
m_xsum_wsum = VEC4_CONST( 0.0f ); m_xsum_wsum = VEC4_CONST( 0.0f );
for( int i = 0; i < count; ++i ) for( int i = 0; i < count; ++i )
{ {
int j = order[i]; int j = order[i];
Vec4 p( unweighted[j].X(), unweighted[j].Y(), unweighted[j].Z(), 1.0f ); Vec4 p( unweighted[j].X(), unweighted[j].Y(), unweighted[j].Z(), 1.0f );
Vec4 w( weights[j] ); Vec4 w( weights[j] );
Vec4 x = p*w; Vec4 x = p*w;
m_points_weights[i] = x; m_points_weights[i] = x;
m_xsum_wsum += x; m_xsum_wsum += x;
} }
return true; return true;
} }
void ClusterFit::Compress3( void* block ) void ClusterFit::Compress3( void* block )
{ {
// declare variables // declare variables
int const count = m_colours->GetCount(); int const count = m_colours->GetCount();
Vec4 const two = VEC4_CONST( 2.0 ); Vec4 const two = VEC4_CONST( 2.0 );
Vec4 const one = VEC4_CONST( 1.0f ); Vec4 const one = VEC4_CONST( 1.0f );
Vec4 const half_half2( 0.5f, 0.5f, 0.5f, 0.25f ); Vec4 const half_half2( 0.5f, 0.5f, 0.5f, 0.25f );
Vec4 const zero = VEC4_CONST( 0.0f ); Vec4 const zero = VEC4_CONST( 0.0f );
Vec4 const half = VEC4_CONST( 0.5f ); Vec4 const half = VEC4_CONST( 0.5f );
Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f ); Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f );
Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f ); Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f );
// prepare an ordering using the principle axis // prepare an ordering using the principle axis
ConstructOrdering( m_principle, 0 ); ConstructOrdering( m_principle, 0 );
// check all possible clusters and iterate on the total order
Vec4 beststart = VEC4_CONST( 0.0f );
Vec4 bestend = VEC4_CONST( 0.0f );
Vec4 besterror = m_besterror;
u8 bestindices[16];
int bestiteration = 0;
int besti = 0, bestj = 0;
// loop over iterations (we avoid the case that all points in first or last cluster)
for( int iterationIndex = 0;; )
{
// first cluster [0,i) is at the start
Vec4 part0 = VEC4_CONST( 0.0f );
for( int i = 0; i < count; ++i )
{
// second cluster [i,j) is half along
Vec4 part1 = ( i == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f );
int jmin = ( i == 0 ) ? 1 : i;
for( int j = jmin;; )
{
// last cluster [j,count) is at the end
Vec4 part2 = m_xsum_wsum - part1 - part0;
// compute least squares terms directly
Vec4 alphax_sum = MultiplyAdd( part1, half_half2, part0 );
Vec4 alpha2_sum = alphax_sum.SplatW();
Vec4 betax_sum = MultiplyAdd( part1, half_half2, part2 ); // check all possible clusters and iterate on the total order
Vec4 beta2_sum = betax_sum.SplatW(); Vec4 beststart = VEC4_CONST( 0.0f );
Vec4 bestend = VEC4_CONST( 0.0f );
Vec4 besterror = m_besterror;
u8 bestindices[16];
int bestiteration = 0;
int besti = 0, bestj = 0;
Vec4 alphabeta_sum = ( part1*half_half2 ).SplatW(); // loop over iterations (we avoid the case that all points in first or last cluster)
for( int iterationIndex = 0;; )
{
// first cluster [0,i) is at the start
Vec4 part0 = VEC4_CONST( 0.0f );
for( int i = 0; i < count; ++i )
{
// second cluster [i,j) is half along
Vec4 part1 = ( i == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f );
int jmin = ( i == 0 ) ? 1 : i;
for( int j = jmin;; )
{
// last cluster [j,count) is at the end
Vec4 part2 = m_xsum_wsum - part1 - part0;
// compute the least-squares optimal points // compute least squares terms directly
Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) ); Vec4 alphax_sum = MultiplyAdd( part1, half_half2, part0 );
Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor; Vec4 alpha2_sum = alphax_sum.SplatW();
Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor;
// clamp to the grid Vec4 betax_sum = MultiplyAdd( part1, half_half2, part2 );
a = Min( one, Max( zero, a ) ); Vec4 beta2_sum = betax_sum.SplatW();
b = Min( one, Max( zero, b ) );
a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp;
b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp;
// compute the error (we skip the constant xxsum)
Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum );
Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum );
Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 );
Vec4 e4 = MultiplyAdd( two, e3, e1 );
// apply the metric to the error term Vec4 alphabeta_sum = ( part1*half_half2 ).SplatW();
Vec4 e5 = e4*m_metric;
Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ();
// keep the solution if it wins
if( CompareAnyLessThan( error, besterror ) )
{
beststart = a;
bestend = b;
besti = i;
bestj = j;
besterror = error;
bestiteration = iterationIndex;
}
// advance // compute the least-squares optimal points
if( j == count ) Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) );
break; Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor;
part1 += m_points_weights[j]; Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor;
++j;
}
// advance // clamp to the grid
part0 += m_points_weights[i]; a = Min( one, Max( zero, a ) );
} b = Min( one, Max( zero, b ) );
a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp;
// stop if we didn't improve in this iteration b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp;
if( bestiteration != iterationIndex )
break;
// advance if possible
++iterationIndex;
if( iterationIndex == m_iterationCount )
break;
// stop if a new iteration is an ordering that has already been tried
Vec3 axis = ( bestend - beststart ).GetVec3();
if( !ConstructOrdering( axis, iterationIndex ) )
break;
}
// save the block if necessary
if( CompareAnyLessThan( besterror, m_besterror ) )
{
// remap the indices
u8 const* order = ( u8* )m_order + 16*bestiteration;
u8 unordered[16]; // compute the error (we skip the constant xxsum)
for( int m = 0; m < besti; ++m ) Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum );
unordered[order[m]] = 0; Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum );
for( int m = besti; m < bestj; ++m ) Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 );
unordered[order[m]] = 2; Vec4 e4 = MultiplyAdd( two, e3, e1 );
for( int m = bestj; m < count; ++m )
unordered[order[m]] = 1;
m_colours->RemapIndices( unordered, bestindices ); // apply the metric to the error term
Vec4 e5 = e4*m_metric;
// save the block Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ();
WriteColourBlock3( beststart.GetVec3(), bestend.GetVec3(), bestindices, block );
// save the error // keep the solution if it wins
m_besterror = besterror; if( CompareAnyLessThan( error, besterror ) )
} {
beststart = a;
bestend = b;
besti = i;
bestj = j;
besterror = error;
bestiteration = iterationIndex;
}
// advance
if( j == count )
break;
part1 += m_points_weights[j];
++j;
}
// advance
part0 += m_points_weights[i];
}
// stop if we didn't improve in this iteration
if( bestiteration != iterationIndex )
break;
// advance if possible
++iterationIndex;
if( iterationIndex == m_iterationCount )
break;
// stop if a new iteration is an ordering that has already been tried
Vec3 axis = ( bestend - beststart ).GetVec3();
if( !ConstructOrdering( axis, iterationIndex ) )
break;
}
// save the block if necessary
if( CompareAnyLessThan( besterror, m_besterror ) )
{
// remap the indices
u8 const* order = ( u8* )m_order + 16*bestiteration;
u8 unordered[16];
for( int m = 0; m < besti; ++m )
unordered[order[m]] = 0;
for( int m = besti; m < bestj; ++m )
unordered[order[m]] = 2;
for( int m = bestj; m < count; ++m )
unordered[order[m]] = 1;
m_colours->RemapIndices( unordered, bestindices );
// save the block
WriteColourBlock3( beststart.GetVec3(), bestend.GetVec3(), bestindices, block );
// save the error
m_besterror = besterror;
}
} }
void ClusterFit::Compress4( void* block ) void ClusterFit::Compress4( void* block )
{ {
// declare variables // declare variables
int const count = m_colours->GetCount(); int const count = m_colours->GetCount();
Vec4 const two = VEC4_CONST( 2.0f ); Vec4 const two = VEC4_CONST( 2.0f );
Vec4 const one = VEC4_CONST( 1.0f ); Vec4 const one = VEC4_CONST( 1.0f );
Vec4 const onethird_onethird2( 1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f, 1.0f/9.0f ); Vec4 const onethird_onethird2( 1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f, 1.0f/9.0f );
Vec4 const twothirds_twothirds2( 2.0f/3.0f, 2.0f/3.0f, 2.0f/3.0f, 4.0f/9.0f ); Vec4 const twothirds_twothirds2( 2.0f/3.0f, 2.0f/3.0f, 2.0f/3.0f, 4.0f/9.0f );
Vec4 const twonineths = VEC4_CONST( 2.0f/9.0f ); Vec4 const twonineths = VEC4_CONST( 2.0f/9.0f );
Vec4 const zero = VEC4_CONST( 0.0f ); Vec4 const zero = VEC4_CONST( 0.0f );
Vec4 const half = VEC4_CONST( 0.5f ); Vec4 const half = VEC4_CONST( 0.5f );
Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f ); Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f );
Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f ); Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f );
// prepare an ordering using the principle axis // prepare an ordering using the principle axis
ConstructOrdering( m_principle, 0 ); ConstructOrdering( m_principle, 0 );
// check all possible clusters and iterate on the total order
Vec4 beststart = VEC4_CONST( 0.0f );
Vec4 bestend = VEC4_CONST( 0.0f );
Vec4 besterror = m_besterror;
u8 bestindices[16];
int bestiteration = 0;
int besti = 0, bestj = 0, bestk = 0;
// loop over iterations (we avoid the case that all points in first or last cluster)
for( int iterationIndex = 0;; )
{
// first cluster [0,i) is at the start
Vec4 part0 = VEC4_CONST( 0.0f );
for( int i = 0; i < count; ++i )
{
// second cluster [i,j) is one third along
Vec4 part1 = VEC4_CONST( 0.0f );
for( int j = i;; )
{
// third cluster [j,k) is two thirds along
Vec4 part2 = ( j == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f );
int kmin = ( j == 0 ) ? 1 : j;
for( int k = kmin;; )
{
// last cluster [k,count) is at the end
Vec4 part3 = m_xsum_wsum - part2 - part1 - part0;
// compute least squares terms directly // check all possible clusters and iterate on the total order
Vec4 const alphax_sum = MultiplyAdd( part2, onethird_onethird2, MultiplyAdd( part1, twothirds_twothirds2, part0 ) ); Vec4 beststart = VEC4_CONST( 0.0f );
Vec4 const alpha2_sum = alphax_sum.SplatW(); Vec4 bestend = VEC4_CONST( 0.0f );
Vec4 besterror = m_besterror;
Vec4 const betax_sum = MultiplyAdd( part1, onethird_onethird2, MultiplyAdd( part2, twothirds_twothirds2, part3 ) ); u8 bestindices[16];
Vec4 const beta2_sum = betax_sum.SplatW(); int bestiteration = 0;
int besti = 0, bestj = 0, bestk = 0;
Vec4 const alphabeta_sum = twonineths*( part1 + part2 ).SplatW();
// compute the least-squares optimal points // loop over iterations (we avoid the case that all points in first or last cluster)
Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) ); for( int iterationIndex = 0;; )
Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor; {
Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor; // first cluster [0,i) is at the start
Vec4 part0 = VEC4_CONST( 0.0f );
for( int i = 0; i < count; ++i )
{
// second cluster [i,j) is one third along
Vec4 part1 = VEC4_CONST( 0.0f );
for( int j = i;; )
{
// third cluster [j,k) is two thirds along
Vec4 part2 = ( j == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f );
int kmin = ( j == 0 ) ? 1 : j;
for( int k = kmin;; )
{
// last cluster [k,count) is at the end
Vec4 part3 = m_xsum_wsum - part2 - part1 - part0;
// clamp to the grid // compute least squares terms directly
a = Min( one, Max( zero, a ) ); Vec4 const alphax_sum = MultiplyAdd( part2, onethird_onethird2, MultiplyAdd( part1, twothirds_twothirds2, part0 ) );
b = Min( one, Max( zero, b ) ); Vec4 const alpha2_sum = alphax_sum.SplatW();
a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp;
b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp;
// compute the error (we skip the constant xxsum)
Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum );
Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum );
Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 );
Vec4 e4 = MultiplyAdd( two, e3, e1 );
// apply the metric to the error term Vec4 const betax_sum = MultiplyAdd( part1, onethird_onethird2, MultiplyAdd( part2, twothirds_twothirds2, part3 ) );
Vec4 e5 = e4*m_metric; Vec4 const beta2_sum = betax_sum.SplatW();
Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ();
// keep the solution if it wins Vec4 const alphabeta_sum = twonineths*( part1 + part2 ).SplatW();
if( CompareAnyLessThan( error, besterror ) )
{
beststart = a;
bestend = b;
besterror = error;
besti = i;
bestj = j;
bestk = k;
bestiteration = iterationIndex;
}
// advance // compute the least-squares optimal points
if( k == count ) Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) );
break; Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor;
part2 += m_points_weights[k]; Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor;
++k;
}
// advance // clamp to the grid
if( j == count ) a = Min( one, Max( zero, a ) );
break; b = Min( one, Max( zero, b ) );
part1 += m_points_weights[j]; a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp;
++j; b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp;
}
// advance // compute the error (we skip the constant xxsum)
part0 += m_points_weights[i]; Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum );
} Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum );
Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 );
// stop if we didn't improve in this iteration Vec4 e4 = MultiplyAdd( two, e3, e1 );
if( bestiteration != iterationIndex )
break;
// advance if possible
++iterationIndex;
if( iterationIndex == m_iterationCount )
break;
// stop if a new iteration is an ordering that has already been tried
Vec3 axis = ( bestend - beststart ).GetVec3();
if( !ConstructOrdering( axis, iterationIndex ) )
break;
}
// save the block if necessary // apply the metric to the error term
if( CompareAnyLessThan( besterror, m_besterror ) ) Vec4 e5 = e4*m_metric;
{ Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ();
// remap the indices
u8 const* order = ( u8* )m_order + 16*bestiteration;
u8 unordered[16]; // keep the solution if it wins
for( int m = 0; m < besti; ++m ) if( CompareAnyLessThan( error, besterror ) )
unordered[order[m]] = 0; {
for( int m = besti; m < bestj; ++m ) beststart = a;
unordered[order[m]] = 2; bestend = b;
for( int m = bestj; m < bestk; ++m ) besterror = error;
unordered[order[m]] = 3; besti = i;
for( int m = bestk; m < count; ++m ) bestj = j;
unordered[order[m]] = 1; bestk = k;
bestiteration = iterationIndex;
}
m_colours->RemapIndices( unordered, bestindices ); // advance
if( k == count )
// save the block break;
WriteColourBlock4( beststart.GetVec3(), bestend.GetVec3(), bestindices, block ); part2 += m_points_weights[k];
++k;
}
// save the error // advance
m_besterror = besterror; if( j == count )
} break;
part1 += m_points_weights[j];
++j;
}
// advance
part0 += m_points_weights[i];
}
// stop if we didn't improve in this iteration
if( bestiteration != iterationIndex )
break;
// advance if possible
++iterationIndex;
if( iterationIndex == m_iterationCount )
break;
// stop if a new iteration is an ordering that has already been tried
Vec3 axis = ( bestend - beststart ).GetVec3();
if( !ConstructOrdering( axis, iterationIndex ) )
break;
}
// save the block if necessary
if( CompareAnyLessThan( besterror, m_besterror ) )
{
// remap the indices
u8 const* order = ( u8* )m_order + 16*bestiteration;
u8 unordered[16];
for( int m = 0; m < besti; ++m )
unordered[order[m]] = 0;
for( int m = besti; m < bestj; ++m )
unordered[order[m]] = 2;
for( int m = bestj; m < bestk; ++m )
unordered[order[m]] = 3;
for( int m = bestk; m < count; ++m )
unordered[order[m]] = 1;
m_colours->RemapIndices( unordered, bestindices );
// save the block
WriteColourBlock4( beststart.GetVec3(), bestend.GetVec3(), bestindices, block );
// save the error
m_besterror = besterror;
}
} }
} // namespace squish } // namespace squish

View File

@ -1,33 +1,33 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Copyright (c) 2007 Ignacio Castano icastano@nvidia.com Copyright (c) 2007 Ignacio Castano icastano@nvidia.com
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_CLUSTERFIT_H #ifndef SQUISH_CLUSTERFIT_H
#define SQUISH_CLUSTERFIT_H #define SQUISH_CLUSTERFIT_H
#include <squish.h> #include "squish.h"
#include "maths.h" #include "maths.h"
#include "simd.h" #include "simd.h"
#include "colourfit.h" #include "colourfit.h"
@ -37,23 +37,23 @@ namespace squish {
class ClusterFit : public ColourFit class ClusterFit : public ColourFit
{ {
public: public:
ClusterFit( ColourSet const* colours, int flags ); ClusterFit( ColourSet const* colours, int flags, float* metric );
private: private:
bool ConstructOrdering( Vec3 const& axis, int iteration ); bool ConstructOrdering( Vec3 const& axis, int iteration );
virtual void Compress3( void* block ); virtual void Compress3( void* block );
virtual void Compress4( void* block ); virtual void Compress4( void* block );
enum { kMaxIterations = 8 }; enum { kMaxIterations = 8 };
int m_iterationCount; int m_iterationCount;
Vec3 m_principle; Vec3 m_principle;
u8 m_order[16*kMaxIterations]; u8 m_order[16*kMaxIterations];
Vec4 m_points_weights[16]; Vec4 m_points_weights[16];
Vec4 m_xsum_wsum; Vec4 m_xsum_wsum;
Vec4 m_metric; Vec4 m_metric;
Vec4 m_besterror; Vec4 m_besterror;
}; };
} // namespace squish } // namespace squish

View File

@ -1,214 +1,214 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#include "colourblock.h" #include "colourblock.h"
namespace squish { namespace squish {
static int FloatToInt( float a, int limit ) static int FloatToInt( float a, int limit )
{ {
// use ANSI round-to-zero behaviour to get round-to-nearest // use ANSI round-to-zero behaviour to get round-to-nearest
int i = ( int )( a + 0.5f ); int i = ( int )( a + 0.5f );
// clamp to the limit // clamp to the limit
if( i < 0 ) if( i < 0 )
i = 0; i = 0;
else if( i > limit ) else if( i > limit )
i = limit; i = limit;
// done // done
return i; return i;
} }
static int FloatTo565( Vec3::Arg colour ) static int FloatTo565( Vec3::Arg colour )
{ {
// get the components in the correct range // get the components in the correct range
int r = FloatToInt( 31.0f*colour.X(), 31 ); int r = FloatToInt( 31.0f*colour.X(), 31 );
int g = FloatToInt( 63.0f*colour.Y(), 63 ); int g = FloatToInt( 63.0f*colour.Y(), 63 );
int b = FloatToInt( 31.0f*colour.Z(), 31 ); int b = FloatToInt( 31.0f*colour.Z(), 31 );
// pack into a single value // pack into a single value
return ( r << 11 ) | ( g << 5 ) | b; return ( r << 11 ) | ( g << 5 ) | b;
} }
static void WriteColourBlock( int a, int b, u8* indices, void* block ) static void WriteColourBlock( int a, int b, u8* indices, void* block )
{ {
// get the block as bytes // get the block as bytes
u8* bytes = ( u8* )block; u8* bytes = ( u8* )block;
// write the endpoints // write the endpoints
bytes[0] = ( u8 )( a & 0xff ); bytes[0] = ( u8 )( a & 0xff );
bytes[1] = ( u8 )( a >> 8 ); bytes[1] = ( u8 )( a >> 8 );
bytes[2] = ( u8 )( b & 0xff ); bytes[2] = ( u8 )( b & 0xff );
bytes[3] = ( u8 )( b >> 8 ); bytes[3] = ( u8 )( b >> 8 );
// write the indices // write the indices
for( int i = 0; i < 4; ++i ) for( int i = 0; i < 4; ++i )
{ {
u8 const* ind = indices + 4*i; u8 const* ind = indices + 4*i;
bytes[4 + i] = ind[0] | ( ind[1] << 2 ) | ( ind[2] << 4 ) | ( ind[3] << 6 ); bytes[4 + i] = ind[0] | ( ind[1] << 2 ) | ( ind[2] << 4 ) | ( ind[3] << 6 );
} }
} }
void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ) void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block )
{ {
// get the packed values // get the packed values
int a = FloatTo565( start ); int a = FloatTo565( start );
int b = FloatTo565( end ); int b = FloatTo565( end );
// remap the indices // remap the indices
u8 remapped[16]; u8 remapped[16];
if( a <= b ) if( a <= b )
{ {
// use the indices directly // use the indices directly
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
remapped[i] = indices[i]; remapped[i] = indices[i];
} }
else else
{ {
// swap a and b // swap a and b
std::swap( a, b ); std::swap( a, b );
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
{ {
if( indices[i] == 0 ) if( indices[i] == 0 )
remapped[i] = 1; remapped[i] = 1;
else if( indices[i] == 1 ) else if( indices[i] == 1 )
remapped[i] = 0; remapped[i] = 0;
else else
remapped[i] = indices[i]; remapped[i] = indices[i];
} }
} }
// write the block // write the block
WriteColourBlock( a, b, remapped, block ); WriteColourBlock( a, b, remapped, block );
} }
void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ) void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block )
{ {
// get the packed values // get the packed values
int a = FloatTo565( start ); int a = FloatTo565( start );
int b = FloatTo565( end ); int b = FloatTo565( end );
// remap the indices // remap the indices
u8 remapped[16]; u8 remapped[16];
if( a < b ) if( a < b )
{ {
// swap a and b // swap a and b
std::swap( a, b ); std::swap( a, b );
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
remapped[i] = ( indices[i] ^ 0x1 ) & 0x3; remapped[i] = ( indices[i] ^ 0x1 ) & 0x3;
} }
else if( a == b ) else if( a == b )
{ {
// use index 0 // use index 0
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
remapped[i] = 0; remapped[i] = 0;
} }
else else
{ {
// use the indices directly // use the indices directly
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
remapped[i] = indices[i]; remapped[i] = indices[i];
} }
// write the block // write the block
WriteColourBlock( a, b, remapped, block ); WriteColourBlock( a, b, remapped, block );
} }
static int Unpack565( u8 const* packed, u8* colour ) static int Unpack565( u8 const* packed, u8* colour )
{ {
// build the packed value // build the packed value
int value = ( int )packed[0] | ( ( int )packed[1] << 8 ); int value = ( int )packed[0] | ( ( int )packed[1] << 8 );
// get the components in the stored range
u8 red = ( u8 )( ( value >> 11 ) & 0x1f );
u8 green = ( u8 )( ( value >> 5 ) & 0x3f );
u8 blue = ( u8 )( value & 0x1f );
// scale up to 8 bits // get the components in the stored range
colour[0] = ( red << 3 ) | ( red >> 2 ); u8 red = ( u8 )( ( value >> 11 ) & 0x1f );
colour[1] = ( green << 2 ) | ( green >> 4 ); u8 green = ( u8 )( ( value >> 5 ) & 0x3f );
colour[2] = ( blue << 3 ) | ( blue >> 2 ); u8 blue = ( u8 )( value & 0x1f );
colour[3] = 255;
// scale up to 8 bits
// return the value colour[0] = ( red << 3 ) | ( red >> 2 );
return value; colour[1] = ( green << 2 ) | ( green >> 4 );
colour[2] = ( blue << 3 ) | ( blue >> 2 );
colour[3] = 255;
// return the value
return value;
} }
void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) void DecompressColour( u8* rgba, void const* block, bool isDxt1 )
{ {
// get the block bytes // get the block bytes
u8 const* bytes = reinterpret_cast< u8 const* >( block ); u8 const* bytes = reinterpret_cast< u8 const* >( block );
// unpack the endpoints
u8 codes[16];
int a = Unpack565( bytes, codes );
int b = Unpack565( bytes + 2, codes + 4 );
// generate the midpoints
for( int i = 0; i < 3; ++i )
{
int c = codes[i];
int d = codes[4 + i];
if( isDxt1 && a <= b ) // unpack the endpoints
{ u8 codes[16];
codes[8 + i] = ( u8 )( ( c + d )/2 ); int a = Unpack565( bytes, codes );
codes[12 + i] = 0; int b = Unpack565( bytes + 2, codes + 4 );
}
else
{
codes[8 + i] = ( u8 )( ( 2*c + d )/3 );
codes[12 + i] = ( u8 )( ( c + 2*d )/3 );
}
}
// fill in alpha for the intermediate values
codes[8 + 3] = 255;
codes[12 + 3] = ( isDxt1 && a <= b ) ? 0 : 255;
// unpack the indices
u8 indices[16];
for( int i = 0; i < 4; ++i )
{
u8* ind = indices + 4*i;
u8 packed = bytes[4 + i];
ind[0] = packed & 0x3;
ind[1] = ( packed >> 2 ) & 0x3;
ind[2] = ( packed >> 4 ) & 0x3;
ind[3] = ( packed >> 6 ) & 0x3;
}
// store out the colours // generate the midpoints
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 3; ++i )
{ {
u8 offset = 4*indices[i]; int c = codes[i];
for( int j = 0; j < 4; ++j ) int d = codes[4 + i];
rgba[4*i + j] = codes[offset + j];
} if( isDxt1 && a <= b )
{
codes[8 + i] = ( u8 )( ( c + d )/2 );
codes[12 + i] = 0;
}
else
{
codes[8 + i] = ( u8 )( ( 2*c + d )/3 );
codes[12 + i] = ( u8 )( ( c + 2*d )/3 );
}
}
// fill in alpha for the intermediate values
codes[8 + 3] = 255;
codes[12 + 3] = ( isDxt1 && a <= b ) ? 0 : 255;
// unpack the indices
u8 indices[16];
for( int i = 0; i < 4; ++i )
{
u8* ind = indices + 4*i;
u8 packed = bytes[4 + i];
ind[0] = packed & 0x3;
ind[1] = ( packed >> 2 ) & 0x3;
ind[2] = ( packed >> 4 ) & 0x3;
ind[3] = ( packed >> 6 ) & 0x3;
}
// store out the colours
for( int i = 0; i < 16; ++i )
{
u8 offset = 4*indices[i];
for( int j = 0; j < 4; ++j )
rgba[4*i + j] = codes[offset + j];
}
} }
} // namespace squish } // namespace squish

View File

@ -1,32 +1,32 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_COLOURBLOCK_H #ifndef SQUISH_COLOURBLOCK_H
#define SQUISH_COLOURBLOCK_H #define SQUISH_COLOURBLOCK_H
#include <squish.h> #include "squish.h"
#include "maths.h" #include "maths.h"
namespace squish { namespace squish {

View File

@ -1,50 +1,54 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#include "colourfit.h" #include "colourfit.h"
#include "colourset.h" #include "colourset.h"
namespace squish { namespace squish {
ColourFit::ColourFit( ColourSet const* colours, int flags ) ColourFit::ColourFit( ColourSet const* colours, int flags )
: m_colours( colours ), : m_colours( colours ),
m_flags( flags ) m_flags( flags )
{
}
ColourFit::~ColourFit()
{ {
} }
void ColourFit::Compress( void* block ) void ColourFit::Compress( void* block )
{ {
bool isDxt1 = ( ( m_flags & kDxt1 ) != 0 ); bool isDxt1 = ( ( m_flags & kDxt1 ) != 0 );
if( isDxt1 ) if( isDxt1 )
{ {
Compress3( block ); Compress3( block );
if( !m_colours->IsTransparent() ) if( !m_colours->IsTransparent() )
Compress4( block ); Compress4( block );
} }
else else
Compress4( block ); Compress4( block );
} }
} // namespace squish } // namespace squish

View File

@ -1,34 +1,36 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_COLOURFIT_H #ifndef SQUISH_COLOURFIT_H
#define SQUISH_COLOURFIT_H #define SQUISH_COLOURFIT_H
#include <squish.h> #include "squish.h"
#include "maths.h" #include "maths.h"
#include <climits>
namespace squish { namespace squish {
class ColourSet; class ColourSet;
@ -36,16 +38,17 @@ class ColourSet;
class ColourFit class ColourFit
{ {
public: public:
ColourFit( ColourSet const* colours, int flags ); ColourFit( ColourSet const* colours, int flags );
virtual ~ColourFit();
void Compress( void* block ); void Compress( void* block );
protected: protected:
virtual void Compress3( void* block ) = 0; virtual void Compress3( void* block ) = 0;
virtual void Compress4( void* block ) = 0; virtual void Compress4( void* block ) = 0;
ColourSet const* m_colours; ColourSet const* m_colours;
int m_flags; int m_flags;
}; };
} // namespace squish } // namespace squish

View File

@ -1,121 +1,121 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#include "colourset.h" #include "colourset.h"
namespace squish { namespace squish {
ColourSet::ColourSet( u8 const* rgba, int mask, int flags ) ColourSet::ColourSet( u8 const* rgba, int mask, int flags )
: m_count( 0 ), : m_count( 0 ),
m_transparent( false ) m_transparent( false )
{ {
// check the compression mode for dxt1 // check the compression mode for dxt1
bool isDxt1 = ( ( flags & kDxt1 ) != 0 ); bool isDxt1 = ( ( flags & kDxt1 ) != 0 );
bool weightByAlpha = ( ( flags & kWeightColourByAlpha ) != 0 ); bool weightByAlpha = ( ( flags & kWeightColourByAlpha ) != 0 );
// create the minimal set // create the minimal set
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
{ {
// check this pixel is enabled // check this pixel is enabled
int bit = 1 << i; int bit = 1 << i;
if( ( mask & bit ) == 0 ) if( ( mask & bit ) == 0 )
{ {
m_remap[i] = -1; m_remap[i] = -1;
continue; continue;
} }
// check for transparent pixels when using dxt1
if( isDxt1 && rgba[4*i + 3] < 128 )
{
m_remap[i] = -1;
m_transparent = true;
continue;
}
// loop over previous points for a match // check for transparent pixels when using dxt1
for( int j = 0;; ++j ) if( isDxt1 && rgba[4*i + 3] < 128 )
{ {
// allocate a new point m_remap[i] = -1;
if( j == i ) m_transparent = true;
{ continue;
// normalise coordinates to [0,1] }
float x = ( float )rgba[4*i] / 255.0f;
float y = ( float )rgba[4*i + 1] / 255.0f;
float z = ( float )rgba[4*i + 2] / 255.0f;
// ensure there is always non-zero weight even for zero alpha
float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f;
// add the point // loop over previous points for a match
m_points[m_count] = Vec3( x, y, z ); for( int j = 0;; ++j )
m_weights[m_count] = ( weightByAlpha ? w : 1.0f ); {
m_remap[i] = m_count; // allocate a new point
if( j == i )
// advance {
++m_count; // normalise coordinates to [0,1]
break; float x = ( float )rgba[4*i] / 255.0f;
} float y = ( float )rgba[4*i + 1] / 255.0f;
float z = ( float )rgba[4*i + 2] / 255.0f;
// check for a match
int oldbit = 1 << j;
bool match = ( ( mask & oldbit ) != 0 )
&& ( rgba[4*i] == rgba[4*j] )
&& ( rgba[4*i + 1] == rgba[4*j + 1] )
&& ( rgba[4*i + 2] == rgba[4*j + 2] )
&& ( rgba[4*j + 3] >= 128 || !isDxt1 );
if( match )
{
// get the index of the match
int index = m_remap[j];
// ensure there is always non-zero weight even for zero alpha
float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f;
// map to this point and increase the weight // ensure there is always non-zero weight even for zero alpha
m_weights[index] += ( weightByAlpha ? w : 1.0f ); float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f;
m_remap[i] = index;
break;
}
}
}
// square root the weights // add the point
for( int i = 0; i < m_count; ++i ) m_points[m_count] = Vec3( x, y, z );
m_weights[i] = std::sqrt( m_weights[i] ); m_weights[m_count] = ( weightByAlpha ? w : 1.0f );
m_remap[i] = m_count;
// advance
++m_count;
break;
}
// check for a match
int oldbit = 1 << j;
bool match = ( ( mask & oldbit ) != 0 )
&& ( rgba[4*i] == rgba[4*j] )
&& ( rgba[4*i + 1] == rgba[4*j + 1] )
&& ( rgba[4*i + 2] == rgba[4*j + 2] )
&& ( rgba[4*j + 3] >= 128 || !isDxt1 );
if( match )
{
// get the index of the match
int index = m_remap[j];
// ensure there is always non-zero weight even for zero alpha
float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f;
// map to this point and increase the weight
m_weights[index] += ( weightByAlpha ? w : 1.0f );
m_remap[i] = index;
break;
}
}
}
// square root the weights
for( int i = 0; i < m_count; ++i )
m_weights[i] = std::sqrt( m_weights[i] );
} }
void ColourSet::RemapIndices( u8 const* source, u8* target ) const void ColourSet::RemapIndices( u8 const* source, u8* target ) const
{ {
for( int i = 0; i < 16; ++i ) for( int i = 0; i < 16; ++i )
{ {
int j = m_remap[i]; int j = m_remap[i];
if( j == -1 ) if( j == -1 )
target[i] = 3; target[i] = 3;
else else
target[i] = source[j]; target[i] = source[j];
} }
} }
} // namespace squish } // namespace squish

View File

@ -1,32 +1,32 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_COLOURSET_H #ifndef SQUISH_COLOURSET_H
#define SQUISH_COLOURSET_H #define SQUISH_COLOURSET_H
#include <squish.h> #include "squish.h"
#include "maths.h" #include "maths.h"
namespace squish { namespace squish {
@ -36,21 +36,21 @@ namespace squish {
class ColourSet class ColourSet
{ {
public: public:
ColourSet( u8 const* rgba, int mask, int flags ); ColourSet( u8 const* rgba, int mask, int flags );
int GetCount() const { return m_count; } int GetCount() const { return m_count; }
Vec3 const* GetPoints() const { return m_points; } Vec3 const* GetPoints() const { return m_points; }
float const* GetWeights() const { return m_weights; } float const* GetWeights() const { return m_weights; }
bool IsTransparent() const { return m_transparent; } bool IsTransparent() const { return m_transparent; }
void RemapIndices( u8 const* source, u8* target ) const; void RemapIndices( u8 const* source, u8* target ) const;
private: private:
int m_count; int m_count;
Vec3 m_points[16]; Vec3 m_points[16];
float m_weights[16]; float m_weights[16];
int m_remap[16]; int m_remap[16];
bool m_transparent; bool m_transparent;
}; };
} // namespace sqish } // namespace sqish

View File

@ -1,28 +1,28 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_CONFIG_H #ifndef SQUISH_CONFIG_H
#define SQUISH_CONFIG_H #define SQUISH_CONFIG_H
@ -36,7 +36,7 @@
#define SQUISH_USE_SSE 0 #define SQUISH_USE_SSE 0
#endif #endif
// Internally et SQUISH_USE_SIMD when either Altivec or SSE is available. // Internally set SQUISH_USE_SIMD when either Altivec or SSE is available.
#if SQUISH_USE_ALTIVEC && SQUISH_USE_SSE #if SQUISH_USE_ALTIVEC && SQUISH_USE_SSE
#error "Cannot enable both Altivec and SSE!" #error "Cannot enable both Altivec and SSE!"
#endif #endif

View File

@ -1,227 +1,259 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
/*! @file /*! @file
The symmetric eigensystem solver algorithm is from The symmetric eigensystem solver algorithm is from
http://www.geometrictools.com/Documentation/EigenSymmetric3x3.pdf http://www.geometrictools.com/Documentation/EigenSymmetric3x3.pdf
*/ */
#include "maths.h" #include "maths.h"
#include "simd.h"
#include <cfloat> #include <cfloat>
namespace squish { namespace squish {
Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights ) Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights )
{ {
// compute the centroid // compute the centroid
float total = 0.0f; float total = 0.0f;
Vec3 centroid( 0.0f ); Vec3 centroid( 0.0f );
for( int i = 0; i < n; ++i ) for( int i = 0; i < n; ++i )
{ {
total += weights[i]; total += weights[i];
centroid += weights[i]*points[i]; centroid += weights[i]*points[i];
} }
centroid /= total; if( total > FLT_EPSILON )
centroid /= total;
// accumulate the covariance matrix // accumulate the covariance matrix
Sym3x3 covariance( 0.0f ); Sym3x3 covariance( 0.0f );
for( int i = 0; i < n; ++i ) for( int i = 0; i < n; ++i )
{ {
Vec3 a = points[i] - centroid; Vec3 a = points[i] - centroid;
Vec3 b = weights[i]*a; Vec3 b = weights[i]*a;
covariance[0] += a.X()*b.X(); covariance[0] += a.X()*b.X();
covariance[1] += a.X()*b.Y(); covariance[1] += a.X()*b.Y();
covariance[2] += a.X()*b.Z(); covariance[2] += a.X()*b.Z();
covariance[3] += a.Y()*b.Y(); covariance[3] += a.Y()*b.Y();
covariance[4] += a.Y()*b.Z(); covariance[4] += a.Y()*b.Z();
covariance[5] += a.Z()*b.Z(); covariance[5] += a.Z()*b.Z();
} }
// return it // return it
return covariance; return covariance;
} }
#if 0
static Vec3 GetMultiplicity1Evector( Sym3x3 const& matrix, float evalue ) static Vec3 GetMultiplicity1Evector( Sym3x3 const& matrix, float evalue )
{ {
// compute M // compute M
Sym3x3 m; Sym3x3 m;
m[0] = matrix[0] - evalue; m[0] = matrix[0] - evalue;
m[1] = matrix[1]; m[1] = matrix[1];
m[2] = matrix[2]; m[2] = matrix[2];
m[3] = matrix[3] - evalue; m[3] = matrix[3] - evalue;
m[4] = matrix[4]; m[4] = matrix[4];
m[5] = matrix[5] - evalue; m[5] = matrix[5] - evalue;
// compute U // compute U
Sym3x3 u; Sym3x3 u;
u[0] = m[3]*m[5] - m[4]*m[4]; u[0] = m[3]*m[5] - m[4]*m[4];
u[1] = m[2]*m[4] - m[1]*m[5]; u[1] = m[2]*m[4] - m[1]*m[5];
u[2] = m[1]*m[4] - m[2]*m[3]; u[2] = m[1]*m[4] - m[2]*m[3];
u[3] = m[0]*m[5] - m[2]*m[2]; u[3] = m[0]*m[5] - m[2]*m[2];
u[4] = m[1]*m[2] - m[4]*m[0]; u[4] = m[1]*m[2] - m[4]*m[0];
u[5] = m[0]*m[3] - m[1]*m[1]; u[5] = m[0]*m[3] - m[1]*m[1];
// find the largest component // find the largest component
float mc = std::fabs( u[0] ); float mc = std::fabs( u[0] );
int mi = 0; int mi = 0;
for( int i = 1; i < 6; ++i ) for( int i = 1; i < 6; ++i )
{ {
float c = std::fabs( u[i] ); float c = std::fabs( u[i] );
if( c > mc ) if( c > mc )
{ {
mc = c; mc = c;
mi = i; mi = i;
} }
} }
// pick the column with this component // pick the column with this component
switch( mi ) switch( mi )
{ {
case 0: case 0:
return Vec3( u[0], u[1], u[2] ); return Vec3( u[0], u[1], u[2] );
case 1: case 1:
case 3: case 3:
return Vec3( u[1], u[3], u[4] ); return Vec3( u[1], u[3], u[4] );
default: default:
return Vec3( u[2], u[4], u[5] ); return Vec3( u[2], u[4], u[5] );
} }
} }
static Vec3 GetMultiplicity2Evector( Sym3x3 const& matrix, float evalue ) static Vec3 GetMultiplicity2Evector( Sym3x3 const& matrix, float evalue )
{ {
// compute M // compute M
Sym3x3 m; Sym3x3 m;
m[0] = matrix[0] - evalue; m[0] = matrix[0] - evalue;
m[1] = matrix[1]; m[1] = matrix[1];
m[2] = matrix[2]; m[2] = matrix[2];
m[3] = matrix[3] - evalue; m[3] = matrix[3] - evalue;
m[4] = matrix[4]; m[4] = matrix[4];
m[5] = matrix[5] - evalue; m[5] = matrix[5] - evalue;
// find the largest component // find the largest component
float mc = std::fabs( m[0] ); float mc = std::fabs( m[0] );
int mi = 0; int mi = 0;
for( int i = 1; i < 6; ++i ) for( int i = 1; i < 6; ++i )
{ {
float c = std::fabs( m[i] ); float c = std::fabs( m[i] );
if( c > mc ) if( c > mc )
{ {
mc = c; mc = c;
mi = i; mi = i;
} }
} }
// pick the first eigenvector based on this index // pick the first eigenvector based on this index
switch( mi ) switch( mi )
{ {
case 0: case 0:
case 1: case 1:
return Vec3( -m[1], m[0], 0.0f ); return Vec3( -m[1], m[0], 0.0f );
case 2: case 2:
return Vec3( m[2], 0.0f, -m[0] ); return Vec3( m[2], 0.0f, -m[0] );
case 3: case 3:
case 4: case 4:
return Vec3( 0.0f, -m[4], m[3] ); return Vec3( 0.0f, -m[4], m[3] );
default: default:
return Vec3( 0.0f, -m[5], m[4] ); return Vec3( 0.0f, -m[5], m[4] );
} }
} }
Vec3 ComputePrincipleComponent( Sym3x3 const& matrix ) Vec3 ComputePrincipleComponent( Sym3x3 const& matrix )
{ {
// compute the cubic coefficients // compute the cubic coefficients
float c0 = matrix[0]*matrix[3]*matrix[5] float c0 = matrix[0]*matrix[3]*matrix[5]
+ 2.0f*matrix[1]*matrix[2]*matrix[4] + 2.0f*matrix[1]*matrix[2]*matrix[4]
- matrix[0]*matrix[4]*matrix[4] - matrix[0]*matrix[4]*matrix[4]
- matrix[3]*matrix[2]*matrix[2] - matrix[3]*matrix[2]*matrix[2]
- matrix[5]*matrix[1]*matrix[1]; - matrix[5]*matrix[1]*matrix[1];
float c1 = matrix[0]*matrix[3] + matrix[0]*matrix[5] + matrix[3]*matrix[5] float c1 = matrix[0]*matrix[3] + matrix[0]*matrix[5] + matrix[3]*matrix[5]
- matrix[1]*matrix[1] - matrix[2]*matrix[2] - matrix[4]*matrix[4]; - matrix[1]*matrix[1] - matrix[2]*matrix[2] - matrix[4]*matrix[4];
float c2 = matrix[0] + matrix[3] + matrix[5]; float c2 = matrix[0] + matrix[3] + matrix[5];
// compute the quadratic coefficients // compute the quadratic coefficients
float a = c1 - ( 1.0f/3.0f )*c2*c2; float a = c1 - ( 1.0f/3.0f )*c2*c2;
float b = ( -2.0f/27.0f )*c2*c2*c2 + ( 1.0f/3.0f )*c1*c2 - c0; float b = ( -2.0f/27.0f )*c2*c2*c2 + ( 1.0f/3.0f )*c1*c2 - c0;
// compute the root count check // compute the root count check
float Q = 0.25f*b*b + ( 1.0f/27.0f )*a*a*a; float Q = 0.25f*b*b + ( 1.0f/27.0f )*a*a*a;
// test the multiplicity // test the multiplicity
if( FLT_EPSILON < Q ) if( FLT_EPSILON < Q )
{ {
// only one root, which implies we have a multiple of the identity // only one root, which implies we have a multiple of the identity
return Vec3( 1.0f ); return Vec3( 1.0f );
} }
else if( Q < -FLT_EPSILON ) else if( Q < -FLT_EPSILON )
{ {
// three distinct roots // three distinct roots
float theta = std::atan2( std::sqrt( -Q ), -0.5f*b ); float theta = std::atan2( std::sqrt( -Q ), -0.5f*b );
float rho = std::sqrt( 0.25f*b*b - Q ); float rho = std::sqrt( 0.25f*b*b - Q );
float rt = std::pow( rho, 1.0f/3.0f ); float rt = std::pow( rho, 1.0f/3.0f );
float ct = std::cos( theta/3.0f ); float ct = std::cos( theta/3.0f );
float st = std::sin( theta/3.0f ); float st = std::sin( theta/3.0f );
float l1 = ( 1.0f/3.0f )*c2 + 2.0f*rt*ct; float l1 = ( 1.0f/3.0f )*c2 + 2.0f*rt*ct;
float l2 = ( 1.0f/3.0f )*c2 - rt*( ct + ( float )sqrt( 3.0f )*st ); float l2 = ( 1.0f/3.0f )*c2 - rt*( ct + ( float )sqrt( 3.0f )*st );
float l3 = ( 1.0f/3.0f )*c2 - rt*( ct - ( float )sqrt( 3.0f )*st ); float l3 = ( 1.0f/3.0f )*c2 - rt*( ct - ( float )sqrt( 3.0f )*st );
// pick the larger // pick the larger
if( std::fabs( l2 ) > std::fabs( l1 ) ) if( std::fabs( l2 ) > std::fabs( l1 ) )
l1 = l2; l1 = l2;
if( std::fabs( l3 ) > std::fabs( l1 ) ) if( std::fabs( l3 ) > std::fabs( l1 ) )
l1 = l3; l1 = l3;
// get the eigenvector // get the eigenvector
return GetMultiplicity1Evector( matrix, l1 ); return GetMultiplicity1Evector( matrix, l1 );
} }
else // if( -FLT_EPSILON <= Q && Q <= FLT_EPSILON ) else // if( -FLT_EPSILON <= Q && Q <= FLT_EPSILON )
{ {
// two roots // two roots
float rt; float rt;
if( b < 0.0f ) if( b < 0.0f )
rt = -std::pow( -0.5f*b, 1.0f/3.0f ); rt = -std::pow( -0.5f*b, 1.0f/3.0f );
else else
rt = std::pow( 0.5f*b, 1.0f/3.0f ); rt = std::pow( 0.5f*b, 1.0f/3.0f );
float l1 = ( 1.0f/3.0f )*c2 + rt; // repeated float l1 = ( 1.0f/3.0f )*c2 + rt; // repeated
float l2 = ( 1.0f/3.0f )*c2 - 2.0f*rt; float l2 = ( 1.0f/3.0f )*c2 - 2.0f*rt;
// get the eigenvector // get the eigenvector
if( std::fabs( l1 ) > std::fabs( l2 ) ) if( std::fabs( l1 ) > std::fabs( l2 ) )
return GetMultiplicity2Evector( matrix, l1 ); return GetMultiplicity2Evector( matrix, l1 );
else else
return GetMultiplicity1Evector( matrix, l2 ); return GetMultiplicity1Evector( matrix, l2 );
} }
} }
#else
#define POWER_ITERATION_COUNT 8
Vec3 ComputePrincipleComponent( Sym3x3 const& matrix )
{
Vec4 const row0( matrix[0], matrix[1], matrix[2], 0.0f );
Vec4 const row1( matrix[1], matrix[3], matrix[4], 0.0f );
Vec4 const row2( matrix[2], matrix[4], matrix[5], 0.0f );
Vec4 v = VEC4_CONST( 1.0f );
for( int i = 0; i < POWER_ITERATION_COUNT; ++i )
{
// matrix multiply
Vec4 w = row0*v.SplatX();
w = MultiplyAdd(row1, v.SplatY(), w);
w = MultiplyAdd(row2, v.SplatZ(), w);
// get max component from xyz in all channels
Vec4 a = Max(w.SplatX(), Max(w.SplatY(), w.SplatZ()));
// divide through and advance
v = w*Reciprocal(a);
}
return v.GetVec3();
}
#endif
} // namespace squish } // namespace squish

View File

@ -1,28 +1,28 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_MATHS_H #ifndef SQUISH_MATHS_H
#define SQUISH_MATHS_H #define SQUISH_MATHS_H
@ -35,194 +35,194 @@ namespace squish {
class Vec3 class Vec3
{ {
public: public:
typedef Vec3 const& Arg; typedef Vec3 const& Arg;
Vec3() Vec3()
{ {
} }
explicit Vec3( float s ) explicit Vec3( float s )
{ {
m_x = s; m_x = s;
m_y = s; m_y = s;
m_z = s; m_z = s;
} }
Vec3( float x, float y, float z ) Vec3( float x, float y, float z )
{ {
m_x = x; m_x = x;
m_y = y; m_y = y;
m_z = z; m_z = z;
} }
float X() const { return m_x; }
float Y() const { return m_y; }
float Z() const { return m_z; }
Vec3 operator-() const
{
return Vec3( -m_x, -m_y, -m_z );
}
Vec3& operator+=( Arg v )
{
m_x += v.m_x;
m_y += v.m_y;
m_z += v.m_z;
return *this;
}
Vec3& operator-=( Arg v )
{
m_x -= v.m_x;
m_y -= v.m_y;
m_z -= v.m_z;
return *this;
}
Vec3& operator*=( Arg v )
{
m_x *= v.m_x;
m_y *= v.m_y;
m_z *= v.m_z;
return *this;
}
Vec3& operator*=( float s )
{
m_x *= s;
m_y *= s;
m_z *= s;
return *this;
}
Vec3& operator/=( Arg v )
{
m_x /= v.m_x;
m_y /= v.m_y;
m_z /= v.m_z;
return *this;
}
Vec3& operator/=( float s )
{
float t = 1.0f/s;
m_x *= t;
m_y *= t;
m_z *= t;
return *this;
}
friend Vec3 operator+( Arg left, Arg right )
{
Vec3 copy( left );
return copy += right;
}
friend Vec3 operator-( Arg left, Arg right )
{
Vec3 copy( left );
return copy -= right;
}
friend Vec3 operator*( Arg left, Arg right )
{
Vec3 copy( left );
return copy *= right;
}
friend Vec3 operator*( Arg left, float right )
{
Vec3 copy( left );
return copy *= right;
}
friend Vec3 operator*( float left, Arg right )
{
Vec3 copy( right );
return copy *= left;
}
friend Vec3 operator/( Arg left, Arg right )
{
Vec3 copy( left );
return copy /= right;
}
friend Vec3 operator/( Arg left, float right )
{
Vec3 copy( left );
return copy /= right;
}
friend float Dot( Arg left, Arg right )
{
return left.m_x*right.m_x + left.m_y*right.m_y + left.m_z*right.m_z;
}
friend Vec3 Min( Arg left, Arg right )
{
return Vec3(
std::min( left.m_x, right.m_x ),
std::min( left.m_y, right.m_y ),
std::min( left.m_z, right.m_z )
);
}
friend Vec3 Max( Arg left, Arg right ) float X() const { return m_x; }
{ float Y() const { return m_y; }
return Vec3( float Z() const { return m_z; }
std::max( left.m_x, right.m_x ),
std::max( left.m_y, right.m_y ),
std::max( left.m_z, right.m_z )
);
}
friend Vec3 Truncate( Arg v ) Vec3 operator-() const
{ {
return Vec3( return Vec3( -m_x, -m_y, -m_z );
v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ), }
v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ),
v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z ) Vec3& operator+=( Arg v )
); {
} m_x += v.m_x;
m_y += v.m_y;
m_z += v.m_z;
return *this;
}
Vec3& operator-=( Arg v )
{
m_x -= v.m_x;
m_y -= v.m_y;
m_z -= v.m_z;
return *this;
}
Vec3& operator*=( Arg v )
{
m_x *= v.m_x;
m_y *= v.m_y;
m_z *= v.m_z;
return *this;
}
Vec3& operator*=( float s )
{
m_x *= s;
m_y *= s;
m_z *= s;
return *this;
}
Vec3& operator/=( Arg v )
{
m_x /= v.m_x;
m_y /= v.m_y;
m_z /= v.m_z;
return *this;
}
Vec3& operator/=( float s )
{
float t = 1.0f/s;
m_x *= t;
m_y *= t;
m_z *= t;
return *this;
}
friend Vec3 operator+( Arg left, Arg right )
{
Vec3 copy( left );
return copy += right;
}
friend Vec3 operator-( Arg left, Arg right )
{
Vec3 copy( left );
return copy -= right;
}
friend Vec3 operator*( Arg left, Arg right )
{
Vec3 copy( left );
return copy *= right;
}
friend Vec3 operator*( Arg left, float right )
{
Vec3 copy( left );
return copy *= right;
}
friend Vec3 operator*( float left, Arg right )
{
Vec3 copy( right );
return copy *= left;
}
friend Vec3 operator/( Arg left, Arg right )
{
Vec3 copy( left );
return copy /= right;
}
friend Vec3 operator/( Arg left, float right )
{
Vec3 copy( left );
return copy /= right;
}
friend float Dot( Arg left, Arg right )
{
return left.m_x*right.m_x + left.m_y*right.m_y + left.m_z*right.m_z;
}
friend Vec3 Min( Arg left, Arg right )
{
return Vec3(
std::min( left.m_x, right.m_x ),
std::min( left.m_y, right.m_y ),
std::min( left.m_z, right.m_z )
);
}
friend Vec3 Max( Arg left, Arg right )
{
return Vec3(
std::max( left.m_x, right.m_x ),
std::max( left.m_y, right.m_y ),
std::max( left.m_z, right.m_z )
);
}
friend Vec3 Truncate( Arg v )
{
return Vec3(
v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ),
v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ),
v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z )
);
}
private: private:
float m_x; float m_x;
float m_y; float m_y;
float m_z; float m_z;
}; };
inline float LengthSquared( Vec3::Arg v ) inline float LengthSquared( Vec3::Arg v )
{ {
return Dot( v, v ); return Dot( v, v );
} }
class Sym3x3 class Sym3x3
{ {
public: public:
Sym3x3() Sym3x3()
{ {
} }
Sym3x3( float s ) Sym3x3( float s )
{ {
for( int i = 0; i < 6; ++i ) for( int i = 0; i < 6; ++i )
m_x[i] = s; m_x[i] = s;
} }
float operator[]( int index ) const float operator[]( int index ) const
{ {
return m_x[index]; return m_x[index];
} }
float& operator[]( int index ) float& operator[]( int index )
{ {
return m_x[index]; return m_x[index];
} }
private: private:
float m_x[6]; float m_x[6];
}; };
Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights ); Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights );

View File

@ -1,28 +1,28 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#include "rangefit.h" #include "rangefit.h"
#include "colourset.h" #include "colourset.h"
#include "colourblock.h" #include "colourblock.h"
@ -30,173 +30,172 @@
namespace squish { namespace squish {
RangeFit::RangeFit( ColourSet const* colours, int flags ) RangeFit::RangeFit( ColourSet const* colours, int flags, float* metric )
: ColourFit( colours, flags ) : ColourFit( colours, flags )
{ {
// initialise the metric // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)
bool perceptual = ( ( m_flags & kColourMetricPerceptual ) != 0 ); if( metric )
if( perceptual ) m_metric = Vec3( metric[0], metric[1], metric[2] );
m_metric = Vec3( 0.2126f, 0.7152f, 0.0722f ); else
else m_metric = Vec3( 1.0f );
m_metric = Vec3( 1.0f );
// initialise the best error // initialise the best error
m_besterror = FLT_MAX; m_besterror = FLT_MAX;
// cache some values // cache some values
int const count = m_colours->GetCount(); int const count = m_colours->GetCount();
Vec3 const* values = m_colours->GetPoints(); Vec3 const* values = m_colours->GetPoints();
float const* weights = m_colours->GetWeights(); float const* weights = m_colours->GetWeights();
// get the covariance matrix
Sym3x3 covariance = ComputeWeightedCovariance( count, values, weights );
// compute the principle component
Vec3 principle = ComputePrincipleComponent( covariance );
// get the min and max range as the codebook endpoints // get the covariance matrix
Vec3 start( 0.0f ); Sym3x3 covariance = ComputeWeightedCovariance( count, values, weights );
Vec3 end( 0.0f );
if( count > 0 )
{
float min, max;
// compute the range
start = end = values[0];
min = max = Dot( values[0], principle );
for( int i = 1; i < count; ++i )
{
float val = Dot( values[i], principle );
if( val < min )
{
start = values[i];
min = val;
}
else if( val > max )
{
end = values[i];
max = val;
}
}
}
// clamp the output to [0, 1]
Vec3 const one( 1.0f );
Vec3 const zero( 0.0f );
start = Min( one, Max( zero, start ) );
end = Min( one, Max( zero, end ) );
// clamp to the grid and save // compute the principle component
Vec3 const grid( 31.0f, 63.0f, 31.0f ); Vec3 principle = ComputePrincipleComponent( covariance );
Vec3 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f );
Vec3 const half( 0.5f ); // get the min and max range as the codebook endpoints
m_start = Truncate( grid*start + half )*gridrcp; Vec3 start( 0.0f );
m_end = Truncate( grid*end + half )*gridrcp; Vec3 end( 0.0f );
if( count > 0 )
{
float min, max;
// compute the range
start = end = values[0];
min = max = Dot( values[0], principle );
for( int i = 1; i < count; ++i )
{
float val = Dot( values[i], principle );
if( val < min )
{
start = values[i];
min = val;
}
else if( val > max )
{
end = values[i];
max = val;
}
}
}
// clamp the output to [0, 1]
Vec3 const one( 1.0f );
Vec3 const zero( 0.0f );
start = Min( one, Max( zero, start ) );
end = Min( one, Max( zero, end ) );
// clamp to the grid and save
Vec3 const grid( 31.0f, 63.0f, 31.0f );
Vec3 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f );
Vec3 const half( 0.5f );
m_start = Truncate( grid*start + half )*gridrcp;
m_end = Truncate( grid*end + half )*gridrcp;
} }
void RangeFit::Compress3( void* block ) void RangeFit::Compress3( void* block )
{ {
// cache some values // cache some values
int const count = m_colours->GetCount(); int const count = m_colours->GetCount();
Vec3 const* values = m_colours->GetPoints(); Vec3 const* values = m_colours->GetPoints();
// create a codebook
Vec3 codes[3];
codes[0] = m_start;
codes[1] = m_end;
codes[2] = 0.5f*m_start + 0.5f*m_end;
// match each point to the closest code // create a codebook
u8 closest[16]; Vec3 codes[3];
float error = 0.0f; codes[0] = m_start;
for( int i = 0; i < count; ++i ) codes[1] = m_end;
{ codes[2] = 0.5f*m_start + 0.5f*m_end;
// find the closest code
float dist = FLT_MAX; // match each point to the closest code
int idx = 0; u8 closest[16];
for( int j = 0; j < 3; ++j ) float error = 0.0f;
{ for( int i = 0; i < count; ++i )
float d = LengthSquared( m_metric*( values[i] - codes[j] ) ); {
if( d < dist ) // find the closest code
{ float dist = FLT_MAX;
dist = d; int idx = 0;
idx = j; for( int j = 0; j < 3; ++j )
} {
} float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
if( d < dist )
// save the index {
closest[i] = ( u8 )idx; dist = d;
idx = j;
// accumulate the error }
error += dist; }
}
// save the index
// save this scheme if it wins closest[i] = ( u8 )idx;
if( error < m_besterror )
{ // accumulate the error
// remap the indices error += dist;
u8 indices[16]; }
m_colours->RemapIndices( closest, indices );
// save this scheme if it wins
// save the block if( error < m_besterror )
WriteColourBlock3( m_start, m_end, indices, block ); {
// remap the indices
// save the error u8 indices[16];
m_besterror = error; m_colours->RemapIndices( closest, indices );
}
// save the block
WriteColourBlock3( m_start, m_end, indices, block );
// save the error
m_besterror = error;
}
} }
void RangeFit::Compress4( void* block ) void RangeFit::Compress4( void* block )
{ {
// cache some values // cache some values
int const count = m_colours->GetCount(); int const count = m_colours->GetCount();
Vec3 const* values = m_colours->GetPoints(); Vec3 const* values = m_colours->GetPoints();
// create a codebook
Vec3 codes[4];
codes[0] = m_start;
codes[1] = m_end;
codes[2] = ( 2.0f/3.0f )*m_start + ( 1.0f/3.0f )*m_end;
codes[3] = ( 1.0f/3.0f )*m_start + ( 2.0f/3.0f )*m_end;
// match each point to the closest code // create a codebook
u8 closest[16]; Vec3 codes[4];
float error = 0.0f; codes[0] = m_start;
for( int i = 0; i < count; ++i ) codes[1] = m_end;
{ codes[2] = ( 2.0f/3.0f )*m_start + ( 1.0f/3.0f )*m_end;
// find the closest code codes[3] = ( 1.0f/3.0f )*m_start + ( 2.0f/3.0f )*m_end;
float dist = FLT_MAX;
int idx = 0;
for( int j = 0; j < 4; ++j )
{
float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
if( d < dist )
{
dist = d;
idx = j;
}
}
// save the index
closest[i] = ( u8 )idx;
// accumulate the error
error += dist;
}
// save this scheme if it wins
if( error < m_besterror )
{
// remap the indices
u8 indices[16];
m_colours->RemapIndices( closest, indices );
// save the block
WriteColourBlock4( m_start, m_end, indices, block );
// save the error // match each point to the closest code
m_besterror = error; u8 closest[16];
} float error = 0.0f;
for( int i = 0; i < count; ++i )
{
// find the closest code
float dist = FLT_MAX;
int idx = 0;
for( int j = 0; j < 4; ++j )
{
float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
if( d < dist )
{
dist = d;
idx = j;
}
}
// save the index
closest[i] = ( u8 )idx;
// accumulate the error
error += dist;
}
// save this scheme if it wins
if( error < m_besterror )
{
// remap the indices
u8 indices[16];
m_colours->RemapIndices( closest, indices );
// save the block
WriteColourBlock4( m_start, m_end, indices, block );
// save the error
m_besterror = error;
}
} }
} // namespace squish } // namespace squish

View File

@ -1,32 +1,32 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_RANGEFIT_H #ifndef SQUISH_RANGEFIT_H
#define SQUISH_RANGEFIT_H #define SQUISH_RANGEFIT_H
#include <squish.h> #include "squish.h"
#include "colourfit.h" #include "colourfit.h"
#include "maths.h" #include "maths.h"
@ -37,16 +37,16 @@ class ColourSet;
class RangeFit : public ColourFit class RangeFit : public ColourFit
{ {
public: public:
RangeFit( ColourSet const* colours, int flags ); RangeFit( ColourSet const* colours, int flags, float* metric );
private: private:
virtual void Compress3( void* block ); virtual void Compress3( void* block );
virtual void Compress4( void* block ); virtual void Compress4( void* block );
Vec3 m_metric; Vec3 m_metric;
Vec3 m_start; Vec3 m_start;
Vec3 m_end; Vec3 m_end;
float m_besterror; float m_besterror;
}; };
} // squish } // squish

View File

@ -1,28 +1,28 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_SIMD_H #ifndef SQUISH_SIMD_H
#define SQUISH_SIMD_H #define SQUISH_SIMD_H

View File

@ -1,28 +1,28 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_SIMD_FLOAT_H #ifndef SQUISH_SIMD_FLOAT_H
#define SQUISH_SIMD_FLOAT_H #define SQUISH_SIMD_FLOAT_H
@ -35,146 +35,146 @@ namespace squish {
class Vec4 class Vec4
{ {
public: public:
typedef Vec4 const& Arg; typedef Vec4 const& Arg;
Vec4() {} Vec4() {}
explicit Vec4( float s ) explicit Vec4( float s )
: m_x( s ), : m_x( s ),
m_y( s ), m_y( s ),
m_z( s ), m_z( s ),
m_w( s ) m_w( s )
{ {
} }
Vec4( float x, float y, float z, float w ) Vec4( float x, float y, float z, float w )
: m_x( x ), : m_x( x ),
m_y( y ), m_y( y ),
m_z( z ), m_z( z ),
m_w( w ) m_w( w )
{ {
} }
Vec3 GetVec3() const Vec3 GetVec3() const
{ {
return Vec3( m_x, m_y, m_z ); return Vec3( m_x, m_y, m_z );
} }
Vec4 SplatX() const { return Vec4( m_x ); } Vec4 SplatX() const { return Vec4( m_x ); }
Vec4 SplatY() const { return Vec4( m_y ); } Vec4 SplatY() const { return Vec4( m_y ); }
Vec4 SplatZ() const { return Vec4( m_z ); } Vec4 SplatZ() const { return Vec4( m_z ); }
Vec4 SplatW() const { return Vec4( m_w ); } Vec4 SplatW() const { return Vec4( m_w ); }
Vec4& operator+=( Arg v )
{
m_x += v.m_x;
m_y += v.m_y;
m_z += v.m_z;
m_w += v.m_w;
return *this;
}
Vec4& operator-=( Arg v )
{
m_x -= v.m_x;
m_y -= v.m_y;
m_z -= v.m_z;
m_w -= v.m_w;
return *this;
}
Vec4& operator*=( Arg v )
{
m_x *= v.m_x;
m_y *= v.m_y;
m_z *= v.m_z;
m_w *= v.m_w;
return *this;
}
friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right )
{
Vec4 copy( left );
return copy += right;
}
friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right )
{
Vec4 copy( left );
return copy -= right;
}
friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
{
Vec4 copy( left );
return copy *= right;
}
//! Returns a*b + c
friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return a*b + c;
}
//! Returns -( a*b - c )
friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return c - a*b;
}
friend Vec4 Reciprocal( Vec4::Arg v )
{
return Vec4(
1.0f/v.m_x,
1.0f/v.m_y,
1.0f/v.m_z,
1.0f/v.m_w
);
}
friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
{
return Vec4(
std::min( left.m_x, right.m_x ),
std::min( left.m_y, right.m_y ),
std::min( left.m_z, right.m_z ),
std::min( left.m_w, right.m_w )
);
}
friend Vec4 Max( Vec4::Arg left, Vec4::Arg right )
{
return Vec4(
std::max( left.m_x, right.m_x ),
std::max( left.m_y, right.m_y ),
std::max( left.m_z, right.m_z ),
std::max( left.m_w, right.m_w )
);
}
friend Vec4 Truncate( Vec4::Arg v )
{
return Vec4(
v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ),
v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ),
v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z ),
v.m_w > 0.0f ? std::floor( v.m_w ) : std::ceil( v.m_w )
);
}
friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right )
{
return left.m_x < right.m_x
|| left.m_y < right.m_y
|| left.m_z < right.m_z
|| left.m_w < right.m_w;
}
Vec4& operator+=( Arg v )
{
m_x += v.m_x;
m_y += v.m_y;
m_z += v.m_z;
m_w += v.m_w;
return *this;
}
Vec4& operator-=( Arg v )
{
m_x -= v.m_x;
m_y -= v.m_y;
m_z -= v.m_z;
m_w -= v.m_w;
return *this;
}
Vec4& operator*=( Arg v )
{
m_x *= v.m_x;
m_y *= v.m_y;
m_z *= v.m_z;
m_w *= v.m_w;
return *this;
}
friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right )
{
Vec4 copy( left );
return copy += right;
}
friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right )
{
Vec4 copy( left );
return copy -= right;
}
friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
{
Vec4 copy( left );
return copy *= right;
}
//! Returns a*b + c
friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return a*b + c;
}
//! Returns -( a*b - c )
friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return c - a*b;
}
friend Vec4 Reciprocal( Vec4::Arg v )
{
return Vec4(
1.0f/v.m_x,
1.0f/v.m_y,
1.0f/v.m_z,
1.0f/v.m_w
);
}
friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
{
return Vec4(
std::min( left.m_x, right.m_x ),
std::min( left.m_y, right.m_y ),
std::min( left.m_z, right.m_z ),
std::min( left.m_w, right.m_w )
);
}
friend Vec4 Max( Vec4::Arg left, Vec4::Arg right )
{
return Vec4(
std::max( left.m_x, right.m_x ),
std::max( left.m_y, right.m_y ),
std::max( left.m_z, right.m_z ),
std::max( left.m_w, right.m_w )
);
}
friend Vec4 Truncate( Vec4::Arg v )
{
return Vec4(
v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ),
v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ),
v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z ),
v.m_w > 0.0f ? std::floor( v.m_w ) : std::ceil( v.m_w )
);
}
friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right )
{
return left.m_x < right.m_x
|| left.m_y < right.m_y
|| left.m_z < right.m_z
|| left.m_w < right.m_w;
}
private: private:
float m_x; float m_x;
float m_y; float m_y;
float m_z; float m_z;
float m_w; float m_w;
}; };
} // namespace squish } // namespace squish

View File

@ -1,28 +1,28 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_SIMD_SSE_H #ifndef SQUISH_SIMD_SSE_H
#define SQUISH_SIMD_SSE_H #define SQUISH_SIMD_SSE_H
@ -31,11 +31,11 @@
#include <emmintrin.h> #include <emmintrin.h>
#endif #endif
#define SQUISH_SSE_SPLAT( a ) \ #define SQUISH_SSE_SPLAT( a ) \
( ( a ) | ( ( a ) << 2 ) | ( ( a ) << 4 ) | ( ( a ) << 6 ) ) ( ( a ) | ( ( a ) << 2 ) | ( ( a ) << 4 ) | ( ( a ) << 6 ) )
#define SQUISH_SSE_SHUF( x, y, z, w ) \ #define SQUISH_SSE_SHUF( x, y, z, w ) \
( ( x ) | ( ( y ) << 2 ) | ( ( z ) << 4 ) | ( ( w ) << 6 ) ) ( ( x ) | ( ( y ) << 2 ) | ( ( z ) << 4 ) | ( ( w ) << 6 ) )
namespace squish { namespace squish {
@ -44,135 +44,135 @@ namespace squish {
class Vec4 class Vec4
{ {
public: public:
typedef Vec4 const& Arg; typedef Vec4 const& Arg;
Vec4() {} Vec4() {}
explicit Vec4( __m128 v ) : m_v( v ) {} explicit Vec4( __m128 v ) : m_v( v ) {}
Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {} Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {}
Vec4& operator=( Vec4 const& arg ) Vec4& operator=( Vec4 const& arg )
{ {
m_v = arg.m_v; m_v = arg.m_v;
return *this; return *this;
} }
explicit Vec4( float s ) : m_v( _mm_set1_ps( s ) ) {} explicit Vec4( float s ) : m_v( _mm_set1_ps( s ) ) {}
Vec4( float x, float y, float z, float w ) : m_v( _mm_setr_ps( x, y, z, w ) ) {} Vec4( float x, float y, float z, float w ) : m_v( _mm_setr_ps( x, y, z, w ) ) {}
Vec3 GetVec3() const Vec3 GetVec3() const
{ {
#ifdef __GNUC__ #ifdef __GNUC__
__attribute__ ((__aligned__ (16))) float c[4]; __attribute__ ((__aligned__ (16))) float c[4];
#else #else
__declspec(align(16)) float c[4]; __declspec(align(16)) float c[4];
#endif #endif
_mm_store_ps( c, m_v ); _mm_store_ps( c, m_v );
return Vec3( c[0], c[1], c[2] ); return Vec3( c[0], c[1], c[2] );
} }
Vec4 SplatX() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 0 ) ) ); }
Vec4 SplatY() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 1 ) ) ); }
Vec4 SplatZ() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 2 ) ) ); }
Vec4 SplatW() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 3 ) ) ); }
Vec4& operator+=( Arg v ) Vec4 SplatX() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 0 ) ) ); }
{ Vec4 SplatY() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 1 ) ) ); }
m_v = _mm_add_ps( m_v, v.m_v ); Vec4 SplatZ() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 2 ) ) ); }
return *this; Vec4 SplatW() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 3 ) ) ); }
}
Vec4& operator-=( Arg v )
{
m_v = _mm_sub_ps( m_v, v.m_v );
return *this;
}
Vec4& operator*=( Arg v )
{
m_v = _mm_mul_ps( m_v, v.m_v );
return *this;
}
friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( _mm_add_ps( left.m_v, right.m_v ) );
}
friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( _mm_sub_ps( left.m_v, right.m_v ) );
}
friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( _mm_mul_ps( left.m_v, right.m_v ) );
}
//! Returns a*b + c
friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return Vec4( _mm_add_ps( _mm_mul_ps( a.m_v, b.m_v ), c.m_v ) );
}
//! Returns -( a*b - c )
friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return Vec4( _mm_sub_ps( c.m_v, _mm_mul_ps( a.m_v, b.m_v ) ) );
}
friend Vec4 Reciprocal( Vec4::Arg v )
{
// get the reciprocal estimate
__m128 estimate = _mm_rcp_ps( v.m_v );
// one round of Newton-Rhaphson refinement Vec4& operator+=( Arg v )
__m128 diff = _mm_sub_ps( _mm_set1_ps( 1.0f ), _mm_mul_ps( estimate, v.m_v ) ); {
return Vec4( _mm_add_ps( _mm_mul_ps( diff, estimate ), estimate ) ); m_v = _mm_add_ps( m_v, v.m_v );
} return *this;
}
friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
{ Vec4& operator-=( Arg v )
return Vec4( _mm_min_ps( left.m_v, right.m_v ) ); {
} m_v = _mm_sub_ps( m_v, v.m_v );
return *this;
friend Vec4 Max( Vec4::Arg left, Vec4::Arg right ) }
{
return Vec4( _mm_max_ps( left.m_v, right.m_v ) ); Vec4& operator*=( Arg v )
} {
m_v = _mm_mul_ps( m_v, v.m_v );
friend Vec4 Truncate( Vec4::Arg v ) return *this;
{ }
friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( _mm_add_ps( left.m_v, right.m_v ) );
}
friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( _mm_sub_ps( left.m_v, right.m_v ) );
}
friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( _mm_mul_ps( left.m_v, right.m_v ) );
}
//! Returns a*b + c
friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return Vec4( _mm_add_ps( _mm_mul_ps( a.m_v, b.m_v ), c.m_v ) );
}
//! Returns -( a*b - c )
friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return Vec4( _mm_sub_ps( c.m_v, _mm_mul_ps( a.m_v, b.m_v ) ) );
}
friend Vec4 Reciprocal( Vec4::Arg v )
{
// get the reciprocal estimate
__m128 estimate = _mm_rcp_ps( v.m_v );
// one round of Newton-Rhaphson refinement
__m128 diff = _mm_sub_ps( _mm_set1_ps( 1.0f ), _mm_mul_ps( estimate, v.m_v ) );
return Vec4( _mm_add_ps( _mm_mul_ps( diff, estimate ), estimate ) );
}
friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( _mm_min_ps( left.m_v, right.m_v ) );
}
friend Vec4 Max( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( _mm_max_ps( left.m_v, right.m_v ) );
}
friend Vec4 Truncate( Vec4::Arg v )
{
#if ( SQUISH_USE_SSE == 1 ) #if ( SQUISH_USE_SSE == 1 )
// convert to ints // convert to ints
__m128 input = v.m_v; __m128 input = v.m_v;
__m64 lo = _mm_cvttps_pi32( input ); __m64 lo = _mm_cvttps_pi32( input );
__m64 hi = _mm_cvttps_pi32( _mm_movehl_ps( input, input ) ); __m64 hi = _mm_cvttps_pi32( _mm_movehl_ps( input, input ) );
// convert to floats // convert to floats
__m128 part = _mm_movelh_ps( input, _mm_cvtpi32_ps( input, hi ) ); __m128 part = _mm_movelh_ps( input, _mm_cvtpi32_ps( input, hi ) );
__m128 truncated = _mm_cvtpi32_ps( part, lo ); __m128 truncated = _mm_cvtpi32_ps( part, lo );
// clear out the MMX multimedia state to allow FP calls later // clear out the MMX multimedia state to allow FP calls later
_mm_empty(); _mm_empty();
return Vec4( truncated ); return Vec4( truncated );
#else #else
// use SSE2 instructions // use SSE2 instructions
return Vec4( _mm_cvtepi32_ps( _mm_cvttps_epi32( v.m_v ) ) ); return Vec4( _mm_cvtepi32_ps( _mm_cvttps_epi32( v.m_v ) ) );
#endif #endif
} }
friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right ) friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right )
{ {
__m128 bits = _mm_cmplt_ps( left.m_v, right.m_v ); __m128 bits = _mm_cmplt_ps( left.m_v, right.m_v );
int value = _mm_movemask_ps( bits ); int value = _mm_movemask_ps( bits );
return value != 0; return value != 0;
} }
private: private:
__m128 m_v; __m128 m_v;
}; };
} // namespace squish } // namespace squish

View File

@ -1,28 +1,28 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_SIMD_VE_H #ifndef SQUISH_SIMD_VE_H
#define SQUISH_SIMD_VE_H #define SQUISH_SIMD_VE_H
@ -31,134 +31,134 @@
namespace squish { namespace squish {
#define VEC4_CONST( X ) Vec4( ( vector float )( X ) ) #define VEC4_CONST( X ) Vec4( ( vector float ){ X } )
class Vec4 class Vec4
{ {
public: public:
typedef Vec4 Arg; typedef Vec4 Arg;
Vec4() {} Vec4() {}
explicit Vec4( vector float v ) : m_v( v ) {} explicit Vec4( vector float v ) : m_v( v ) {}
Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {} Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {}
Vec4& operator=( Vec4 const& arg ) Vec4& operator=( Vec4 const& arg )
{ {
m_v = arg.m_v; m_v = arg.m_v;
return *this; return *this;
} }
explicit Vec4( float s ) explicit Vec4( float s )
{ {
union { vector float v; float c[4]; } u; union { vector float v; float c[4]; } u;
u.c[0] = s; u.c[0] = s;
u.c[1] = s; u.c[1] = s;
u.c[2] = s; u.c[2] = s;
u.c[3] = s; u.c[3] = s;
m_v = u.v; m_v = u.v;
} }
Vec4( float x, float y, float z, float w ) Vec4( float x, float y, float z, float w )
{ {
union { vector float v; float c[4]; } u; union { vector float v; float c[4]; } u;
u.c[0] = x; u.c[0] = x;
u.c[1] = y; u.c[1] = y;
u.c[2] = z; u.c[2] = z;
u.c[3] = w; u.c[3] = w;
m_v = u.v; m_v = u.v;
} }
Vec3 GetVec3() const Vec3 GetVec3() const
{ {
union { vector float v; float c[4]; } u; union { vector float v; float c[4]; } u;
u.v = m_v; u.v = m_v;
return Vec3( u.c[0], u.c[1], u.c[2] ); return Vec3( u.c[0], u.c[1], u.c[2] );
} }
Vec4 SplatX() const { return Vec4( vec_splat( m_v, 0 ) ); } Vec4 SplatX() const { return Vec4( vec_splat( m_v, 0 ) ); }
Vec4 SplatY() const { return Vec4( vec_splat( m_v, 1 ) ); } Vec4 SplatY() const { return Vec4( vec_splat( m_v, 1 ) ); }
Vec4 SplatZ() const { return Vec4( vec_splat( m_v, 2 ) ); } Vec4 SplatZ() const { return Vec4( vec_splat( m_v, 2 ) ); }
Vec4 SplatW() const { return Vec4( vec_splat( m_v, 3 ) ); } Vec4 SplatW() const { return Vec4( vec_splat( m_v, 3 ) ); }
Vec4& operator+=( Arg v )
{
m_v = vec_add( m_v, v.m_v );
return *this;
}
Vec4& operator-=( Arg v )
{
m_v = vec_sub( m_v, v.m_v );
return *this;
}
Vec4& operator*=( Arg v )
{
m_v = vec_madd( m_v, v.m_v, ( vector float ){ -0.0f } );
return *this;
}
friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_add( left.m_v, right.m_v ) );
}
friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_sub( left.m_v, right.m_v ) );
}
friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_madd( left.m_v, right.m_v, ( vector float ){ -0.0f } ) );
}
//! Returns a*b + c
friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return Vec4( vec_madd( a.m_v, b.m_v, c.m_v ) );
}
//! Returns -( a*b - c )
friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return Vec4( vec_nmsub( a.m_v, b.m_v, c.m_v ) );
}
friend Vec4 Reciprocal( Vec4::Arg v )
{
// get the reciprocal estimate
vector float estimate = vec_re( v.m_v );
// one round of Newton-Rhaphson refinement
vector float diff = vec_nmsub( estimate, v.m_v, ( vector float ){ 1.0f } );
return Vec4( vec_madd( diff, estimate, estimate ) );
}
friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_min( left.m_v, right.m_v ) );
}
friend Vec4 Max( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_max( left.m_v, right.m_v ) );
}
friend Vec4 Truncate( Vec4::Arg v )
{
return Vec4( vec_trunc( v.m_v ) );
}
friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right )
{
return vec_any_lt( left.m_v, right.m_v ) != 0;
}
Vec4& operator+=( Arg v )
{
m_v = vec_add( m_v, v.m_v );
return *this;
}
Vec4& operator-=( Arg v )
{
m_v = vec_sub( m_v, v.m_v );
return *this;
}
Vec4& operator*=( Arg v )
{
m_v = vec_madd( m_v, v.m_v, ( vector float )( -0.0f ) );
return *this;
}
friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_add( left.m_v, right.m_v ) );
}
friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_sub( left.m_v, right.m_v ) );
}
friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_madd( left.m_v, right.m_v, ( vector float )( -0.0f ) ) );
}
//! Returns a*b + c
friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return Vec4( vec_madd( a.m_v, b.m_v, c.m_v ) );
}
//! Returns -( a*b - c )
friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
{
return Vec4( vec_nmsub( a.m_v, b.m_v, c.m_v ) );
}
friend Vec4 Reciprocal( Vec4::Arg v )
{
// get the reciprocal estimate
vector float estimate = vec_re( v.m_v );
// one round of Newton-Rhaphson refinement
vector float diff = vec_nmsub( estimate, v.m_v, ( vector float )( 1.0f ) );
return Vec4( vec_madd( diff, estimate, estimate ) );
}
friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_min( left.m_v, right.m_v ) );
}
friend Vec4 Max( Vec4::Arg left, Vec4::Arg right )
{
return Vec4( vec_max( left.m_v, right.m_v ) );
}
friend Vec4 Truncate( Vec4::Arg v )
{
return Vec4( vec_trunc( v.m_v ) );
}
friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right )
{
return vec_any_lt( left.m_v, right.m_v ) != 0;
}
private: private:
vector float m_v; vector float m_v;
}; };
} // namespace squish } // namespace squish

View File

@ -1,173 +1,172 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#include "singlecolourfit.h" #include "singlecolourfit.h"
#include "colourset.h" #include "colourset.h"
#include "colourblock.h" #include "colourblock.h"
#include <climits>
namespace squish { namespace squish {
struct SourceBlock struct SourceBlock
{ {
u8 start; u8 start;
u8 end; u8 end;
u8 error; u8 error;
}; };
struct SingleColourLookup struct SingleColourLookup
{ {
SourceBlock sources[2]; SourceBlock sources[2];
}; };
#include "singlecolourlookup.inl" #include "singlecolourlookup.inl"
static int FloatToInt( float a, int limit ) static int FloatToInt( float a, int limit )
{ {
// use ANSI round-to-zero behaviour to get round-to-nearest // use ANSI round-to-zero behaviour to get round-to-nearest
int i = ( int )( a + 0.5f ); int i = ( int )( a + 0.5f );
// clamp to the limit // clamp to the limit
if( i < 0 ) if( i < 0 )
i = 0; i = 0;
else if( i > limit ) else if( i > limit )
i = limit; i = limit;
// done // done
return i; return i;
} }
SingleColourFit::SingleColourFit( ColourSet const* colours, int flags ) SingleColourFit::SingleColourFit( ColourSet const* colours, int flags )
: ColourFit( colours, flags ) : ColourFit( colours, flags )
{ {
// grab the single colour // grab the single colour
Vec3 const* values = m_colours->GetPoints(); Vec3 const* values = m_colours->GetPoints();
m_colour[0] = ( u8 )FloatToInt( 255.0f*values->X(), 255 ); m_colour[0] = ( u8 )FloatToInt( 255.0f*values->X(), 255 );
m_colour[1] = ( u8 )FloatToInt( 255.0f*values->Y(), 255 ); m_colour[1] = ( u8 )FloatToInt( 255.0f*values->Y(), 255 );
m_colour[2] = ( u8 )FloatToInt( 255.0f*values->Z(), 255 ); m_colour[2] = ( u8 )FloatToInt( 255.0f*values->Z(), 255 );
// initialise the best error // initialise the best error
m_besterror = INT_MAX; m_besterror = INT_MAX;
} }
void SingleColourFit::Compress3( void* block ) void SingleColourFit::Compress3( void* block )
{ {
// build the table of lookups // build the table of lookups
SingleColourLookup const* const lookups[] = SingleColourLookup const* const lookups[] =
{ {
lookup_5_3, lookup_5_3,
lookup_6_3, lookup_6_3,
lookup_5_3 lookup_5_3
}; };
// find the best end-points and index
ComputeEndPoints( lookups );
// build the block if we win
if( m_error < m_besterror )
{
// remap the indices
u8 indices[16];
m_colours->RemapIndices( &m_index, indices );
// save the block
WriteColourBlock3( m_start, m_end, indices, block );
// save the error // find the best end-points and index
m_besterror = m_error; ComputeEndPoints( lookups );
}
// build the block if we win
if( m_error < m_besterror )
{
// remap the indices
u8 indices[16];
m_colours->RemapIndices( &m_index, indices );
// save the block
WriteColourBlock3( m_start, m_end, indices, block );
// save the error
m_besterror = m_error;
}
} }
void SingleColourFit::Compress4( void* block ) void SingleColourFit::Compress4( void* block )
{ {
// build the table of lookups // build the table of lookups
SingleColourLookup const* const lookups[] = SingleColourLookup const* const lookups[] =
{ {
lookup_5_4, lookup_5_4,
lookup_6_4, lookup_6_4,
lookup_5_4 lookup_5_4
}; };
// find the best end-points and index
ComputeEndPoints( lookups );
// build the block if we win
if( m_error < m_besterror )
{
// remap the indices
u8 indices[16];
m_colours->RemapIndices( &m_index, indices );
// save the block
WriteColourBlock4( m_start, m_end, indices, block );
// save the error // find the best end-points and index
m_besterror = m_error; ComputeEndPoints( lookups );
}
// build the block if we win
if( m_error < m_besterror )
{
// remap the indices
u8 indices[16];
m_colours->RemapIndices( &m_index, indices );
// save the block
WriteColourBlock4( m_start, m_end, indices, block );
// save the error
m_besterror = m_error;
}
} }
void SingleColourFit::ComputeEndPoints( SingleColourLookup const* const* lookups ) void SingleColourFit::ComputeEndPoints( SingleColourLookup const* const* lookups )
{ {
// check each index combination (endpoint or intermediate) // check each index combination (endpoint or intermediate)
m_error = INT_MAX; m_error = INT_MAX;
for( int index = 0; index < 2; ++index ) for( int index = 0; index < 2; ++index )
{ {
// check the error for this codebook index // check the error for this codebook index
SourceBlock const* sources[3]; SourceBlock const* sources[3];
int error = 0; int error = 0;
for( int channel = 0; channel < 3; ++channel ) for( int channel = 0; channel < 3; ++channel )
{ {
// grab the lookup table and index for this channel // grab the lookup table and index for this channel
SingleColourLookup const* lookup = lookups[channel]; SingleColourLookup const* lookup = lookups[channel];
int target = m_colour[channel]; int target = m_colour[channel];
// store a pointer to the source for this channel // store a pointer to the source for this channel
sources[channel] = lookup[target].sources + index; sources[channel] = lookup[target].sources + index;
// accumulate the error // accumulate the error
int diff = sources[channel]->error; int diff = sources[channel]->error;
error += diff*diff; error += diff*diff;
} }
// keep it if the error is lower // keep it if the error is lower
if( error < m_error ) if( error < m_error )
{ {
m_start = Vec3( m_start = Vec3(
( float )sources[0]->start/31.0f, ( float )sources[0]->start/31.0f,
( float )sources[1]->start/63.0f, ( float )sources[1]->start/63.0f,
( float )sources[2]->start/31.0f ( float )sources[2]->start/31.0f
); );
m_end = Vec3( m_end = Vec3(
( float )sources[0]->end/31.0f, ( float )sources[0]->end/31.0f,
( float )sources[1]->end/63.0f, ( float )sources[1]->end/63.0f,
( float )sources[2]->end/31.0f ( float )sources[2]->end/31.0f
); );
m_index = ( u8 )( 2*index ); m_index = ( u8 )( 2*index );
m_error = error; m_error = error;
} }
} }
} }
} // namespace squish } // namespace squish

View File

@ -1,32 +1,32 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_SINGLECOLOURFIT_H #ifndef SQUISH_SINGLECOLOURFIT_H
#define SQUISH_SINGLECOLOURFIT_H #define SQUISH_SINGLECOLOURFIT_H
#include <squish.h> #include "squish.h"
#include "colourfit.h" #include "colourfit.h"
namespace squish { namespace squish {
@ -37,20 +37,20 @@ struct SingleColourLookup;
class SingleColourFit : public ColourFit class SingleColourFit : public ColourFit
{ {
public: public:
SingleColourFit( ColourSet const* colours, int flags ); SingleColourFit( ColourSet const* colours, int flags );
private: private:
virtual void Compress3( void* block ); virtual void Compress3( void* block );
virtual void Compress4( void* block ); virtual void Compress4( void* block );
void ComputeEndPoints( SingleColourLookup const* const* lookups ); void ComputeEndPoints( SingleColourLookup const* const* lookups );
u8 m_colour[3]; u8 m_colour[3];
Vec3 m_start; Vec3 m_start;
Vec3 m_end; Vec3 m_end;
u8 m_index; u8 m_index;
int m_error; int m_error;
int m_besterror; int m_besterror;
}; };
} // namespace squish } // namespace squish

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +1,30 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#include <squish.h> #include <string.h>
#include "squish.h"
#include "colourset.h" #include "colourset.h"
#include "maths.h" #include "maths.h"
#include "rangefit.h" #include "rangefit.h"
@ -36,204 +37,359 @@ namespace squish {
static int FixFlags( int flags ) static int FixFlags( int flags )
{ {
// grab the flag bits // grab the flag bits
int method = flags & ( kDxt1 | kDxt3 | kDxt5 ); int method = flags & ( kDxt1 | kDxt3 | kDxt5 | kBc4 | kBc5 );
int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit ); int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit );
int metric = flags & ( kColourMetricPerceptual | kColourMetricUniform ); int extra = flags & kWeightColourByAlpha;
int extra = flags & kWeightColourByAlpha;
// set defaults
// set defaults if ( method != kDxt3
if( method != kDxt3 && method != kDxt5 ) && method != kDxt5
method = kDxt1; && method != kBc4
if( fit != kColourRangeFit ) && method != kBc5 )
fit = kColourClusterFit; {
if( metric != kColourMetricUniform ) method = kDxt1;
metric = kColourMetricPerceptual; }
if( fit != kColourRangeFit && fit != kColourIterativeClusterFit )
// done fit = kColourClusterFit;
return method | fit | metric | extra;
// done
return method | fit | extra;
} }
void Compress( u8 const* rgba, void* block, int flags ) void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric )
{ {
// compress with full mask // fix any bad flags
CompressMasked( rgba, 0xffff, block, flags ); flags = FixFlags( flags );
}
void CompressMasked( u8 const* rgba, int mask, void* block, int flags ) if ( ( flags & ( kBc4 | kBc5 ) ) != 0 )
{ {
// fix any bad flags u8 alpha[16*4];
flags = FixFlags( flags ); for( int i = 0; i < 16; ++i )
{
alpha[i*4 + 3] = rgba[i*4 + 0]; // copy R to A
}
// get the block locations u8* rBlock = reinterpret_cast< u8* >( block );
void* colourBlock = block; CompressAlphaDxt5( alpha, mask, rBlock );
void* alphaBock = block;
if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
colourBlock = reinterpret_cast< u8* >( block ) + 8;
// create the minimal point set if ( ( flags & ( kBc5 ) ) != 0 )
ColourSet colours( rgba, mask, flags ); {
for( int i = 0; i < 16; ++i )
// check the compression type and compress colour {
if( colours.GetCount() == 1 ) alpha[i*4 + 3] = rgba[i*4 + 1]; // copy G to A
{ }
// always do a single colour fit
SingleColourFit fit( &colours, flags ); u8* gBlock = reinterpret_cast< u8* >( block ) + 8;
fit.Compress( colourBlock ); CompressAlphaDxt5( alpha, mask, gBlock );
} }
else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 )
{ return;
// do a range fit }
RangeFit fit( &colours, flags );
fit.Compress( colourBlock ); // get the block locations
} void* colourBlock = block;
else void* alphaBlock = block;
{ if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
// default to a cluster fit (could be iterative or not) colourBlock = reinterpret_cast< u8* >( block ) + 8;
ClusterFit fit( &colours, flags );
fit.Compress( colourBlock ); // create the minimal point set
} ColourSet colours( rgba, mask, flags );
// compress alpha separately if necessary // check the compression type and compress colour
if( ( flags & kDxt3 ) != 0 ) if( colours.GetCount() == 1 )
CompressAlphaDxt3( rgba, mask, alphaBock ); {
else if( ( flags & kDxt5 ) != 0 ) // always do a single colour fit
CompressAlphaDxt5( rgba, mask, alphaBock ); SingleColourFit fit( &colours, flags );
fit.Compress( colourBlock );
}
else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 )
{
// do a range fit
RangeFit fit( &colours, flags, metric );
fit.Compress( colourBlock );
}
else
{
// default to a cluster fit (could be iterative or not)
ClusterFit fit( &colours, flags, metric );
fit.Compress( colourBlock );
}
// compress alpha separately if necessary
if( ( flags & kDxt3 ) != 0 )
CompressAlphaDxt3( rgba, mask, alphaBlock );
else if( ( flags & kDxt5 ) != 0 )
CompressAlphaDxt5( rgba, mask, alphaBlock );
} }
void Decompress( u8* rgba, void const* block, int flags ) void Decompress( u8* rgba, void const* block, int flags )
{ {
// fix any bad flags // fix any bad flags
flags = FixFlags( flags ); flags = FixFlags( flags );
// get the block locations // get the block locations
void const* colourBlock = block; void const* colourBlock = block;
void const* alphaBock = block; void const* alphaBlock = block;
if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 ) if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
colourBlock = reinterpret_cast< u8 const* >( block ) + 8; colourBlock = reinterpret_cast< u8 const* >( block ) + 8;
// decompress colour // decompress colour
DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
// decompress alpha separately if necessary // decompress alpha separately if necessary
if( ( flags & kDxt3 ) != 0 ) if( ( flags & kDxt3 ) != 0 )
DecompressAlphaDxt3( rgba, alphaBock ); DecompressAlphaDxt3( rgba, alphaBlock );
else if( ( flags & kDxt5 ) != 0 ) else if( ( flags & kDxt5 ) != 0 )
DecompressAlphaDxt5( rgba, alphaBock ); DecompressAlphaDxt5( rgba, alphaBlock );
} }
int GetStorageRequirements( int width, int height, int flags ) int GetStorageRequirements( int width, int height, int flags )
{ {
// fix any bad flags // fix any bad flags
flags = FixFlags( flags ); flags = FixFlags( flags );
// compute the storage requirements // compute the storage requirements
int blockcount = ( ( width + 3 )/4 ) * ( ( height + 3 )/4 ); int blockcount = ( ( width + 3 )/4 ) * ( ( height + 3 )/4 );
int blocksize = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; int blocksize = ( ( flags & ( kDxt1 | kBc4 ) ) != 0 ) ? 8 : 16;
return blockcount*blocksize; return blockcount*blocksize;
} }
void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags ) void CopyRGBA( u8 const* source, u8* dest, int flags )
{ {
// fix any bad flags if (flags & kSourceBGRA)
flags = FixFlags( flags ); {
// convert from bgra to rgba
dest[0] = source[2];
dest[1] = source[1];
dest[2] = source[0];
dest[3] = source[3];
}
else
{
for( int i = 0; i < 4; ++i )
*dest++ = *source++;
}
}
// initialise the block output void CompressImage( u8 const* rgba, int width, int height, int pitch, void* blocks, int flags, float* metric )
u8* targetBlock = reinterpret_cast< u8* >( blocks ); {
int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; // fix any bad flags
flags = FixFlags( flags );
// loop over blocks // initialise the block output
for( int y = 0; y < height; y += 4 ) u8* targetBlock = reinterpret_cast< u8* >( blocks );
{ int bytesPerBlock = ( ( flags & ( kDxt1 | kBc4 ) ) != 0 ) ? 8 : 16;
for( int x = 0; x < width; x += 4 )
{ // loop over blocks
// build the 4x4 block of pixels for( int y = 0; y < height; y += 4 )
u8 sourceRgba[16*4]; {
u8* targetPixel = sourceRgba; for( int x = 0; x < width; x += 4 )
int mask = 0; {
for( int py = 0; py < 4; ++py ) // build the 4x4 block of pixels
{ u8 sourceRgba[16*4];
for( int px = 0; px < 4; ++px ) u8* targetPixel = sourceRgba;
{ int mask = 0;
// get the source pixel in the image for( int py = 0; py < 4; ++py )
int sx = x + px; {
int sy = y + py; for( int px = 0; px < 4; ++px )
{
// enable if we're in the image // get the source pixel in the image
if( sx < width && sy < height ) int sx = x + px;
{ int sy = y + py;
// copy the rgba value
u8 const* sourcePixel = rgba + 4*( width*sy + sx ); // enable if we're in the image
for( int i = 0; i < 4; ++i ) if( sx < width && sy < height )
*targetPixel++ = *sourcePixel++; {
// copy the rgba value
// enable this pixel u8 const* sourcePixel = rgba + pitch*sy + 4*sx;
mask |= ( 1 << ( 4*py + px ) ); CopyRGBA(sourcePixel, targetPixel, flags);
} // enable this pixel
else mask |= ( 1 << ( 4*py + px ) );
{ }
// skip this pixel as its outside the image
targetPixel += 4; // advance to the next pixel
} targetPixel += 4;
} }
} }
// compress it into the output // compress it into the output
CompressMasked( sourceRgba, mask, targetBlock, flags ); CompressMasked( sourceRgba, mask, targetBlock, flags, metric );
// advance // advance
targetBlock += bytesPerBlock; targetBlock += bytesPerBlock;
} }
} }
}
void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric )
{
CompressImage(rgba, width, height, width*4, blocks, flags, metric);
}
void DecompressImage( u8* rgba, int width, int height, int pitch, void const* blocks, int flags )
{
// fix any bad flags
flags = FixFlags( flags );
// initialise the block input
u8 const* sourceBlock = reinterpret_cast< u8 const* >( blocks );
int bytesPerBlock = ( ( flags & ( kDxt1 | kBc4 ) ) != 0 ) ? 8 : 16;
// loop over blocks
for( int y = 0; y < height; y += 4 )
{
for( int x = 0; x < width; x += 4 )
{
// decompress the block
u8 targetRgba[4*16];
Decompress( targetRgba, sourceBlock, flags );
// write the decompressed pixels to the correct image locations
u8 const* sourcePixel = targetRgba;
for( int py = 0; py < 4; ++py )
{
for( int px = 0; px < 4; ++px )
{
// get the target location
int sx = x + px;
int sy = y + py;
// write if we're in the image
if( sx < width && sy < height )
{
// copy the rgba value
u8* targetPixel = rgba + pitch*sy + 4*sx;
CopyRGBA(sourcePixel, targetPixel, flags);
}
// advance to the next pixel
sourcePixel += 4;
}
}
// advance
sourceBlock += bytesPerBlock;
}
}
} }
void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags ) void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags )
{ {
// fix any bad flags DecompressImage( rgba, width, height, width*4, blocks, flags );
flags = FixFlags( flags ); }
// initialise the block input static double ErrorSq(double x, double y)
u8 const* sourceBlock = reinterpret_cast< u8 const* >( blocks ); {
int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; return (x - y) * (x - y);
}
// loop over blocks static void ComputeBlockWMSE(u8 const *original, u8 const *compressed, unsigned int w, unsigned int h, double &cmse, double &amse)
for( int y = 0; y < height; y += 4 ) {
{ // Computes the MSE for the block and weights it by the variance of the original block.
for( int x = 0; x < width; x += 4 ) // If the variance of the original block is less than 4 (i.e. a standard deviation of 1 per channel)
{ // then the block is close to being a single colour. Quantisation errors in single colour blocks
// decompress the block // are easier to see than similar errors in blocks that contain more colours, particularly when there
u8 targetRgba[4*16]; // are many such blocks in a large area (eg a blue sky background) as they cause banding. Given that
Decompress( targetRgba, sourceBlock, flags ); // banding is easier to see than small errors in "complex" blocks, we weight the errors by a factor
// of 5. This implies that images with large, single colour areas will have a higher potential WMSE
// write the decompressed pixels to the correct image locations // than images with lots of detail.
u8 const* sourcePixel = targetRgba;
for( int py = 0; py < 4; ++py ) cmse = amse = 0;
{ unsigned int sum_p[4]; // per channel sum of pixels
for( int px = 0; px < 4; ++px ) unsigned int sum_p2[4]; // per channel sum of pixels squared
{ memset(sum_p, 0, sizeof(sum_p));
// get the target location memset(sum_p2, 0, sizeof(sum_p2));
int sx = x + px; for( unsigned int py = 0; py < 4; ++py )
int sy = y + py; {
if( sx < width && sy < height ) for( unsigned int px = 0; px < 4; ++px )
{ {
u8* targetPixel = rgba + 4*( width*sy + sx ); if( px < w && py < h )
{
// copy the rgba value double pixelCMSE = 0;
for( int i = 0; i < 4; ++i ) for( int i = 0; i < 3; ++i )
*targetPixel++ = *sourcePixel++; {
} pixelCMSE += ErrorSq(original[i], compressed[i]);
else sum_p[i] += original[i];
{ sum_p2[i] += (unsigned int)original[i]*original[i];
// skip this pixel as its outside the image }
sourcePixel += 4; if( original[3] == 0 && compressed[3] == 0 )
} pixelCMSE = 0; // transparent in both, so colour is inconsequential
} amse += ErrorSq(original[3], compressed[3]);
} cmse += pixelCMSE;
sum_p[3] += original[3];
// advance sum_p2[3] += (unsigned int)original[3]*original[3];
sourceBlock += bytesPerBlock; }
} original += 4;
} compressed += 4;
}
}
unsigned int variance = 0;
for( int i = 0; i < 4; ++i )
variance += w*h*sum_p2[i] - sum_p[i]*sum_p[i];
if( variance < 4 * w * w * h * h )
{
amse *= 5;
cmse *= 5;
}
}
void ComputeMSE( u8 const *rgba, int width, int height, int pitch, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE )
{
// fix any bad flags
flags = FixFlags( flags );
colourMSE = alphaMSE = 0;
// initialise the block input
squish::u8 const* sourceBlock = dxt;
int bytesPerBlock = ( ( flags & squish::kDxt1 ) != 0 ) ? 8 : 16;
// loop over blocks
for( int y = 0; y < height; y += 4 )
{
for( int x = 0; x < width; x += 4 )
{
// decompress the block
u8 targetRgba[4*16];
Decompress( targetRgba, sourceBlock, flags );
u8 const* sourcePixel = targetRgba;
// copy across to a similar pixel block
u8 originalRgba[4*16];
u8* originalPixel = originalRgba;
for( int py = 0; py < 4; ++py )
{
for( int px = 0; px < 4; ++px )
{
int sx = x + px;
int sy = y + py;
if( sx < width && sy < height )
{
u8 const* targetPixel = rgba + pitch*sy + 4*sx;
CopyRGBA(targetPixel, originalPixel, flags);
}
sourcePixel += 4;
originalPixel += 4;
}
}
// compute the weighted MSE of the block
double blockCMSE, blockAMSE;
ComputeBlockWMSE(originalRgba, targetRgba, std::min(4, width - x), std::min(4, height - y), blockCMSE, blockAMSE);
colourMSE += blockCMSE;
alphaMSE += blockAMSE;
// advance
sourceBlock += bytesPerBlock;
}
}
colourMSE /= (width * height * 3);
alphaMSE /= (width * height);
}
void ComputeMSE( u8 const *rgba, int width, int height, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE )
{
ComputeMSE(rgba, width, height, width*4, dxt, flags, colourMSE, alphaMSE);
} }
} // namespace squish } // namespace squish

View File

@ -1,28 +1,28 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be included The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifndef SQUISH_H #ifndef SQUISH_H
#define SQUISH_H #define SQUISH_H
@ -38,124 +38,143 @@ typedef unsigned char u8;
enum enum
{ {
//! Use DXT1 compression. //! Use DXT1 compression.
kDxt1 = ( 1 << 0 ), kDxt1 = ( 1 << 0 ),
//! Use DXT3 compression.
kDxt3 = ( 1 << 1 ),
//! Use DXT5 compression.
kDxt5 = ( 1 << 2 ),
//! Use a very slow but very high quality colour compressor.
kColourIterativeClusterFit = ( 1 << 8 ),
//! Use a slow but high quality colour compressor (the default).
kColourClusterFit = ( 1 << 3 ),
//! Use a fast but low quality colour compressor.
kColourRangeFit = ( 1 << 4 ),
//! Use a perceptual metric for colour error (the default).
kColourMetricPerceptual = ( 1 << 5 ),
//! Use a uniform metric for colour error. //! Use DXT3 compression.
kColourMetricUniform = ( 1 << 6 ), kDxt3 = ( 1 << 1 ),
//! Weight the colour by alpha during cluster fit (disabled by default). //! Use DXT5 compression.
kWeightColourByAlpha = ( 1 << 7 ) kDxt5 = ( 1 << 2 ),
//! Use BC4 compression.
kBc4 = ( 1 << 3 ),
//! Use BC5 compression.
kBc5 = ( 1 << 4 ),
//! Use a slow but high quality colour compressor (the default).
kColourClusterFit = ( 1 << 5 ),
//! Use a fast but low quality colour compressor.
kColourRangeFit = ( 1 << 6 ),
//! Weight the colour by alpha during cluster fit (disabled by default).
kWeightColourByAlpha = ( 1 << 7 ),
//! Use a very slow but very high quality colour compressor.
kColourIterativeClusterFit = ( 1 << 8 ),
//! Source is BGRA rather than RGBA
kSourceBGRA = ( 1 << 9 )
}; };
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! @brief Compresses a 4x4 block of pixels. /*! @brief Compresses a 4x4 block of pixels.
@param rgba The rgba values of the 16 source pixels. @param rgba The rgba values of the 16 source pixels.
@param block Storage for the compressed DXT block. @param mask The valid pixel mask.
@param flags Compression flags. @param block Storage for the compressed DXT block.
@param flags Compression flags.
The source pixels should be presented as a contiguous array of 16 rgba @param metric An optional perceptual metric.
values, with each component as 1 byte each. In memory this should be:
The source pixels should be presented as a contiguous array of 16 rgba
{ r1, g1, b1, a1, .... , r16, g16, b16, a16 } values, with each component as 1 byte each. In memory this should be:
The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, { r1, g1, b1, a1, .... , r16, g16, b16, a16 }
however, DXT1 will be used by default if none is specified. When using DXT1
compression, 8 bytes of storage are required for the compressed DXT block. The mask parameter enables only certain pixels within the block. The lowest
DXT3 and DXT5 compression require 16 bytes of storage per block. bit enables the first pixel and so on up to the 16th bit. Bits beyond the
16th bit are ignored. Pixels that are not enabled are allowed to take
The flags parameter can also specify a preferred colour compressor and arbitrary colours in the output block. An example of how this can be used
colour error metric to use when fitting the RGB components of the data. is in the CompressImage function to disable pixels outside the bounds of
Possible colour compressors are: kColourClusterFit (the default), the image when the width or height is not divisible by 4.
kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics
are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
flags are specified in any particular category then the default will be however, DXT1 will be used by default if none is specified. When using DXT1
used. Unknown flags are ignored. compression, 8 bytes of storage are required for the compressed DXT block.
DXT3 and DXT5 compression require 16 bytes of storage per block.
When using kColourClusterFit, an additional flag can be specified to
weight the colour of each pixel by its alpha value. For images that are The flags parameter can also specify a preferred colour compressor to use
rendered using alpha blending, this can significantly increase the when fitting the RGB components of the data. Possible colour compressors
perceived quality. are: kColourClusterFit (the default), kColourRangeFit (very fast, low
quality) or kColourIterativeClusterFit (slowest, best quality).
When using kColourClusterFit or kColourIterativeClusterFit, an additional
flag can be specified to weight the importance of each pixel by its alpha
value. For images that are rendered using alpha blending, this can
significantly increase the perceived quality.
The metric parameter can be used to weight the relative importance of each
colour channel, or pass NULL to use the default uniform weight of
{ 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that
allowed either uniform or "perceptual" weights with the fixed values
{ 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a
contiguous array of 3 floats.
*/ */
void Compress( u8 const* rgba, void* block, int flags ); void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric = 0 );
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! @brief Compresses a 4x4 block of pixels. /*! @brief Compresses a 4x4 block of pixels.
@param rgba The rgba values of the 16 source pixels. @param rgba The rgba values of the 16 source pixels.
@param mask The valid pixel mask. @param block Storage for the compressed DXT block.
@param block Storage for the compressed DXT block. @param flags Compression flags.
@param flags Compression flags. @param metric An optional perceptual metric.
The source pixels should be presented as a contiguous array of 16 rgba The source pixels should be presented as a contiguous array of 16 rgba
values, with each component as 1 byte each. In memory this should be: values, with each component as 1 byte each. In memory this should be:
{ r1, g1, b1, a1, .... , r16, g16, b16, a16 } { r1, g1, b1, a1, .... , r16, g16, b16, a16 }
The mask parameter enables only certain pixels within the block. The lowest The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
bit enables the first pixel and so on up to the 16th bit. Bits beyond the however, DXT1 will be used by default if none is specified. When using DXT1
16th bit are ignored. Pixels that are not enabled are allowed to take compression, 8 bytes of storage are required for the compressed DXT block.
arbitrary colours in the output block. An example of how this can be used DXT3 and DXT5 compression require 16 bytes of storage per block.
is in the CompressImage function to disable pixels outside the bounds of
the image when the width or height is not divisible by 4. The flags parameter can also specify a preferred colour compressor to use
when fitting the RGB components of the data. Possible colour compressors
The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, are: kColourClusterFit (the default), kColourRangeFit (very fast, low
however, DXT1 will be used by default if none is specified. When using DXT1 quality) or kColourIterativeClusterFit (slowest, best quality).
compression, 8 bytes of storage are required for the compressed DXT block.
DXT3 and DXT5 compression require 16 bytes of storage per block. When using kColourClusterFit or kColourIterativeClusterFit, an additional
flag can be specified to weight the importance of each pixel by its alpha
The flags parameter can also specify a preferred colour compressor and value. For images that are rendered using alpha blending, this can
colour error metric to use when fitting the RGB components of the data. significantly increase the perceived quality.
Possible colour compressors are: kColourClusterFit (the default),
kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics The metric parameter can be used to weight the relative importance of each
are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no colour channel, or pass NULL to use the default uniform weight of
flags are specified in any particular category then the default will be { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that
used. Unknown flags are ignored. allowed either uniform or "perceptual" weights with the fixed values
{ 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a
When using kColourClusterFit, an additional flag can be specified to contiguous array of 3 floats.
weight the colour of each pixel by its alpha value. For images that are
rendered using alpha blending, this can significantly increase the This method is an inline that calls CompressMasked with a mask of 0xffff,
perceived quality. provided for compatibility with older versions of squish.
*/ */
void CompressMasked( u8 const* rgba, int mask, void* block, int flags ); inline void Compress( u8 const* rgba, void* block, int flags, float* metric = 0 )
{
CompressMasked( rgba, 0xffff, block, flags, metric );
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! @brief Decompresses a 4x4 block of pixels. /*! @brief Decompresses a 4x4 block of pixels.
@param rgba Storage for the 16 decompressed pixels. @param rgba Storage for the 16 decompressed pixels.
@param block The compressed DXT block. @param block The compressed DXT block.
@param flags Compression flags. @param flags Compression flags.
The decompressed pixels will be written as a contiguous array of 16 rgba The decompressed pixels will be written as a contiguous array of 16 rgba
values, with each component as 1 byte each. In memory this is: values, with each component as 1 byte each. In memory this is:
{ r1, g1, b1, a1, .... , r16, g16, b16, a16 } { r1, g1, b1, a1, .... , r16, g16, b16, a16 }
The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
however, DXT1 will be used by default if none is specified. All other flags however, DXT1 will be used by default if none is specified. All other flags
are ignored. are ignored.
*/ */
void Decompress( u8* rgba, void const* block, int flags ); void Decompress( u8* rgba, void const* block, int flags );
@ -163,17 +182,17 @@ void Decompress( u8* rgba, void const* block, int flags );
/*! @brief Computes the amount of compressed storage required. /*! @brief Computes the amount of compressed storage required.
@param width The width of the image. @param width The width of the image.
@param height The height of the image. @param height The height of the image.
@param flags Compression flags. @param flags Compression flags.
The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
however, DXT1 will be used by default if none is specified. All other flags however, DXT1 will be used by default if none is specified. All other flags
are ignored. are ignored.
Most DXT images will be a multiple of 4 in each dimension, but this Most DXT images will be a multiple of 4 in each dimension, but this
function supports arbitrary size images by allowing the outer blocks to function supports arbitrary size images by allowing the outer blocks to
be only partially used. be only partially used.
*/ */
int GetStorageRequirements( int width, int height, int flags ); int GetStorageRequirements( int width, int height, int flags );
@ -181,67 +200,101 @@ int GetStorageRequirements( int width, int height, int flags );
/*! @brief Compresses an image in memory. /*! @brief Compresses an image in memory.
@param rgba The pixels of the source. @param rgba The pixels of the source.
@param width The width of the source image. @param width The width of the source image.
@param height The height of the source image. @param height The height of the source image.
@param blocks Storage for the compressed output. @param pitch The pitch of the source image.
@param flags Compression flags. @param blocks Storage for the compressed output.
@param flags Compression flags.
The source pixels should be presented as a contiguous array of width*height @param metric An optional perceptual metric.
rgba values, with each component as 1 byte each. In memory this should be:
The source pixels should be presented as a contiguous array of width*height
{ r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height rgba values, with each component as 1 byte each. In memory this should be:
The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, { r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height
however, DXT1 will be used by default if none is specified. When using DXT1
compression, 8 bytes of storage are required for each compressed DXT block. The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
DXT3 and DXT5 compression require 16 bytes of storage per block. however, DXT1 will be used by default if none is specified. When using DXT1
compression, 8 bytes of storage are required for each compressed DXT block.
The flags parameter can also specify a preferred colour compressor and DXT3 and DXT5 compression require 16 bytes of storage per block.
colour error metric to use when fitting the RGB components of the data.
Possible colour compressors are: kColourClusterFit (the default), The flags parameter can also specify a preferred colour compressor to use
kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics when fitting the RGB components of the data. Possible colour compressors
are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no are: kColourClusterFit (the default), kColourRangeFit (very fast, low
flags are specified in any particular category then the default will be quality) or kColourIterativeClusterFit (slowest, best quality).
used. Unknown flags are ignored.
When using kColourClusterFit or kColourIterativeClusterFit, an additional
When using kColourClusterFit, an additional flag can be specified to flag can be specified to weight the importance of each pixel by its alpha
weight the colour of each pixel by its alpha value. For images that are value. For images that are rendered using alpha blending, this can
rendered using alpha blending, this can significantly increase the significantly increase the perceived quality.
perceived quality.
The metric parameter can be used to weight the relative importance of each
Internally this function calls squish::Compress for each block. To see how colour channel, or pass NULL to use the default uniform weight of
much memory is required in the compressed image, use { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that
squish::GetStorageRequirements. allowed either uniform or "perceptual" weights with the fixed values
{ 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a
contiguous array of 3 floats.
Internally this function calls squish::CompressMasked for each block, which
allows for pixels outside the image to take arbitrary values. The function
squish::GetStorageRequirements can be called to compute the amount of memory
to allocate for the compressed output.
*/ */
void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags ); void CompressImage( u8 const* rgba, int width, int height, int pitch, void* blocks, int flags, float* metric = 0 );
void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric = 0 );
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! @brief Decompresses an image in memory. /*! @brief Decompresses an image in memory.
@param rgba Storage for the decompressed pixels. @param rgba Storage for the decompressed pixels.
@param width The width of the source image. @param width The width of the source image.
@param height The height of the source image. @param height The height of the source image.
@param blocks The compressed DXT blocks. @param pitch The pitch of the decompressed pixels.
@param flags Compression flags. @param blocks The compressed DXT blocks.
@param flags Compression flags.
The decompressed pixels will be written as a contiguous array of width*height
16 rgba values, with each component as 1 byte each. In memory this is:
{ r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height
The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression,
however, DXT1 will be used by default if none is specified. All other flags
are ignored.
Internally this function calls squish::Decompress for each block. The decompressed pixels will be written as a contiguous array of width*height
16 rgba values, with each component as 1 byte each. In memory this is:
{ r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height
The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
however, DXT1 will be used by default if none is specified. All other flags
are ignored.
Internally this function calls squish::Decompress for each block.
*/ */
void DecompressImage( u8* rgba, int width, int height, int pitch, void const* blocks, int flags );
void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags ); void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags );
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! @brief Computes MSE of an compressed image in memory.
@param rgba The original image pixels.
@param width The width of the source image.
@param height The height of the source image.
@param pitch The pitch of the source image.
@param dxt The compressed dxt blocks
@param flags Compression flags.
@param colourMSE The MSE of the colour values.
@param alphaMSE The MSE of the alpha values.
The colour MSE and alpha MSE are computed across all pixels. The colour MSE is
averaged across all rgb values (i.e. colourMSE = sum sum_k ||dxt.k - rgba.k||/3)
The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
however, DXT1 will be used by default if none is specified. All other flags
are ignored.
Internally this function calls squish::Decompress for each block.
*/
void ComputeMSE(u8 const *rgba, int width, int height, int pitch, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE);
void ComputeMSE(u8 const *rgba, int width, int height, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE);
// -----------------------------------------------------------------------------
} // namespace squish } // namespace squish
#endif // ndef SQUISH_H #endif // ndef SQUISH_H