diff -ru squish-1.10/alpha.cpp libsquish/alpha.cpp--- squish-1.10/alpha.cpp 2006-06-29 08:43:24.000000000 -0400+++ libsquish/alpha.cpp 2009-01-04 12:29:26.000000000 -0500@@ -24,6 +24,7 @@ -------------------------------------------------------------------------- */ #include "alpha.h"+#include <limits.h> #include <algorithm> namespace squish {@@ -106,10 +107,10 @@ // check this pixel is valid int bit = 1 << i; if( ( mask & bit ) == 0 )- {- // use the first code- indices[i] = 0;- continue;+ {+ // use the first code+ indices[i] = 0;+ continue; } // find the least error and corresponding indexdiff -ru squish-1.10/clusterfit.cpp libsquish/clusterfit.cpp--- squish-1.10/clusterfit.cpp 2007-03-23 06:19:20.000000000 -0400+++ libsquish/clusterfit.cpp 2008-07-13 06:31:21.000000000 -0400@@ -31,22 +31,21 @@ namespace squish {-ClusterFit::ClusterFit( ColourSet const* colours, int flags ) +ClusterFit::ClusterFit( ColourSet const* colours, int flags, float* metric ) : ColourFit( colours, flags ) { // set the iteration count m_iterationCount = ( m_flags & kColourIterativeClusterFit ) ? kMaxIterations : 1;- // initialise the best error- m_besterror = VEC4_CONST( FLT_MAX );-- // initialise the metric- bool perceptual = ( ( m_flags & kColourMetricPerceptual ) != 0 );- if( perceptual )- m_metric = Vec4( 0.2126f, 0.7152f, 0.0722f, 0.0f );+ // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)+ if( metric )+ m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f ); else m_metric = VEC4_CONST( 1.0f ); + // initialise the best error+ m_besterror = VEC4_CONST( FLT_MAX );+ // cache some values int const count = m_colours->GetCount(); Vec3 const* values = m_colours->GetPoints();diff -ru squish-1.10/clusterfit.h libsquish/clusterfit.h--- squish-1.10/clusterfit.h 2007-03-23 06:19:20.000000000 -0400+++ libsquish/clusterfit.h 2008-07-13 06:31:21.000000000 -0400@@ -37,7 +37,7 @@ class ClusterFit : public ColourFit { public:- ClusterFit( ColourSet const* colours, int flags );+ ClusterFit( ColourSet const* colours, int flags, float* metric ); private: bool ConstructOrdering( Vec3 const& axis, int iteration );Only in libsquish/: CMakeLists.txtdiff -ru squish-1.10/colourfit.cpp libsquish/colourfit.cpp--- squish-1.10/colourfit.cpp 2006-04-07 13:30:11.000000000 -0400+++ libsquish/colourfit.cpp 2009-04-01 17:16:51.000000000 -0400@@ -34,6 +34,10 @@ { }+ColourFit::~ColourFit()+{+}+ void ColourFit::Compress( void* block ) { bool isDxt1 = ( ( m_flags & kDxt1 ) != 0 );diff -ru squish-1.10/colourfit.h libsquish/colourfit.h--- squish-1.10/colourfit.h 2006-04-07 13:30:11.000000000 -0400+++ libsquish/colourfit.h 2009-04-01 17:16:51.000000000 -0400@@ -27,6 +27,7 @@ #define SQUISH_COLOURFIT_H #include <squish.h>+#include <limits.h> #include "maths.h" namespace squish {@@ -37,6 +38,7 @@ { public: ColourFit( ColourSet const* colours, int flags );+ virtual ~ColourFit(); void Compress( void* block );diff -ru squish-1.10/config libsquish/config--- squish-1.10/config 2006-10-01 15:40:09.000000000 -0400+++ libsquish/config 2009-04-20 04:42:39.000000000 -0400@@ -19,4 +19,3 @@ # where should we install to INSTALL_DIR ?= /usr/local-diff -ru squish-1.10/config.h libsquish/config.h--- squish-1.10/config.h 2006-10-01 15:40:09.000000000 -0400+++ libsquish/config.h 2009-01-04 12:29:26.000000000 -0500@@ -36,7 +36,7 @@ #define SQUISH_USE_SSE 0 #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 #error "Cannot enable both Altivec and SSE!" #endifdiff -ru squish-1.10/extra/squishpng.cpp libsquish/extra/squishpng.cpp--- squish-1.10/extra/squishpng.cpp 2007-03-21 15:31:51.000000000 -0400+++ libsquish/extra/squishpng.cpp 2009-04-02 17:36:15.000000000 -0400@@ -25,11 +25,13 @@ /*! @file- @brief Example program that converts between the PNG and DXT formats.+ @brief Test program that compresses images loaded using the PNG format.- This program requires libpng for PNG input and output, and is designed- to show how to prepare data for the squish library when it is not simply- a contiguous block of memory.+ This program requires libpng for PNG input and output, and is designed to+ test the RMS error for DXT compression for a set of test images.++ This program uses the high-level image compression and decompression+ functions that process an entire image at a time. */ #include <iostream>@@ -74,8 +76,16 @@ class Mem : NonCopyable { public:+ Mem() : m_p( 0 ) {} explicit Mem( int size ) : m_p( new u8[size] ) {} ~Mem() { delete[] m_p; }++ void Reset( int size )+ {+ u8 *p = new u8[size];+ delete m_p;+ m_p = p;+ } u8* Get() const { return m_p; }@@ -172,53 +182,71 @@ class PngRows : NonCopyable { public:- PngRows( int width, int height, int stride ) : m_width( width ), m_height( height )+ PngRows( int pitch, int height ) : m_height( height ) {- m_rows = ( png_bytep* )malloc( m_height*sizeof( png_bytep ) );+ m_rows = new png_bytep[m_height]; for( int i = 0; i < m_height; ++i )- m_rows[i] = ( png_bytep )malloc( m_width*stride );+ m_rows[i] = new png_byte[pitch]; } ~PngRows() { for( int i = 0; i < m_height; ++i )- free( m_rows[i] );- free( m_rows );+ delete[] m_rows[i];+ delete[] m_rows; } png_bytep* Get() const { return m_rows; }++ png_bytep operator[](int y) const { return m_rows[y]; } private: png_bytep* m_rows;- int m_width, m_height;+ int m_height; };-class PngImage+//! Represents a DXT compressed image in memory.+struct DxtData+{+ int width;+ int height;+ int format; //!< Either kDxt1, kDxt3 or kDxt5.+ Mem data;+ bool isColour;+ bool isAlpha;+};++//! Represents an uncompressed RGBA image in memory.+class Image { public:- explicit PngImage( std::string const& fileName );+ Image();- int GetWidth() const { return m_width; }- int GetHeight() const { return m_height; }- int GetStride() const { return m_stride; }- bool IsColour() const { return m_colour; }- bool IsAlpha() const { return m_alpha; }- - u8 const* GetRow( int row ) const { return ( u8* )m_rows[row]; }+ void LoadPng( std::string const& fileName );+ void SavePng( std::string const& fileName ) const;-private:- PngReadStruct m_png;+ void Decompress( DxtData const& dxt );+ void Compress( DxtData& dxt, int flags ) const;++ double GetRmsError( Image const& image ) const;+private: int m_width; int m_height;- int m_stride;- bool m_colour;- bool m_alpha;- - png_bytep* m_rows;+ bool m_isColour; //!< Either colour or luminance.+ bool m_isAlpha; //!< Either alpha or not.+ Mem m_pixels; };-PngImage::PngImage( std::string const& fileName )+Image::Image() + : m_width( 0 ), + m_height( 0 ),+ m_isColour( false ),+ m_isAlpha( false )+{+}++void Image::LoadPng( std::string const& fileName ) { // open the source file File file( fopen( fileName.c_str(), "rb" ) );@@ -231,7 +259,9 @@ // check the signature bytes png_byte header[8];- fread( header, 1, 8, file.Get() );+ size_t check = fread( header, 1, 8, file.Get() );+ if( check != 8 )+ throw Error( "file read error" ); if( png_sig_cmp( header, 0, 8 ) ) { std::ostringstream oss;@@ -240,16 +270,17 @@ } // read the image into memory- png_init_io( m_png.GetPng(), file.Get() );- png_set_sig_bytes( m_png.GetPng(), 8 );- png_read_png( m_png.GetPng(), m_png.GetInfo(), PNG_TRANSFORM_EXPAND, 0 );+ PngReadStruct png;+ png_init_io( png.GetPng(), file.Get() );+ png_set_sig_bytes( png.GetPng(), 8 );+ png_read_png( png.GetPng(), png.GetInfo(), PNG_TRANSFORM_EXPAND, 0 ); // get the image info png_uint_32 width; png_uint_32 height; int bitDepth; int colourType;- png_get_IHDR( m_png.GetPng(), m_png.GetInfo(), &width, &height, &bitDepth, &colourType, 0, 0, 0 );+ png_get_IHDR( png.GetPng(), png.GetInfo(), &width, &height, &bitDepth, &colourType, 0, 0, 0 ); // check the image is 8 bit if( bitDepth != 8 )@@ -258,235 +289,157 @@ oss << "cannot process " << bitDepth << "-bit image (bit depth must be 8)"; throw Error( oss.str() ); }- - // save the info++ // copy the data into a contiguous array m_width = width; m_height = height;- m_colour = ( ( colourType & PNG_COLOR_MASK_COLOR ) != 0 );- m_alpha = ( ( colourType & PNG_COLOR_MASK_ALPHA ) != 0 );- m_stride = ( m_colour ? 3 : 1 ) + ( m_alpha ? 1 : 0 );+ m_isColour = ( ( colourType & PNG_COLOR_MASK_COLOR ) != 0 );+ m_isAlpha = ( ( colourType & PNG_COLOR_MASK_ALPHA ) != 0 );+ m_pixels.Reset(4*width*height); // get the image rows- m_rows = png_get_rows( m_png.GetPng(), m_png.GetInfo() );- if( !m_rows )+ png_bytep const *rows = png_get_rows( png.GetPng(), png.GetInfo() );+ if( !rows ) throw Error( "failed to get image rows" );-}--static void Compress( std::string const& sourceFileName, std::string const& targetFileName, int flags )-{- // load the source image- PngImage sourceImage( sourceFileName );-- // get the image info- int width = sourceImage.GetWidth();- int height = sourceImage.GetHeight();- int stride = sourceImage.GetStride();- bool colour = sourceImage.IsColour();- bool alpha = sourceImage.IsAlpha();- // check the image dimensions- if( ( width % 4 ) != 0 || ( height % 4 ) != 0 )+ // copy the pixels into the storage+ u8 *dest = m_pixels.Get();+ for( int y = 0; y < m_height; ++y ) {- std::ostringstream oss;- oss << "cannot compress " << width << "x" << height- << "image (dimensions must be multiples of 4)";- throw Error( oss.str() );- }- - // create the target data- int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;- int targetDataSize = bytesPerBlock*width*height/16;- Mem targetData( targetDataSize );- - // loop over blocks and compress them- clock_t start = std::clock();- u8* targetBlock = targetData.Get();- for( int y = 0; y < height; y += 4 )- {- // process a row of blocks- for( int x = 0; x < width; x += 4 )+ u8 const *src = rows[y];+ for( int x = 0; x < m_width; ++x ) {- // get the block data- u8 sourceRgba[16*4];- for( int py = 0, i = 0; py < 4; ++py )+ if( m_isColour ) {- u8 const* row = sourceImage.GetRow( y + py ) + x*stride;- for( int px = 0; px < 4; ++px, ++i )- {- // get the pixel colour - if( colour )- {- for( int j = 0; j < 3; ++j )- sourceRgba[4*i + j] = *row++;- }- else- {- for( int j = 0; j < 3; ++j )- sourceRgba[4*i + j] = *row;- ++row;- }- - // skip alpha for now- if( alpha )- sourceRgba[4*i + 3] = *row++;- else- sourceRgba[4*i + 3] = 255;- }+ dest[0] = src[0];+ dest[1] = src[1];+ dest[2] = src[2];+ src += 3;+ }+ else+ {+ u8 lum = *src++;+ dest[0] = lum;+ dest[1] = lum;+ dest[2] = lum; }- // compress this block- Compress( sourceRgba, targetBlock, flags );- - // advance- targetBlock += bytesPerBlock; + if( m_isAlpha )+ dest[3] = *src++;+ else+ dest[3] = 255;++ dest += 4; } }- clock_t end = std::clock();- double duration = ( double )( end - start ) / CLOCKS_PER_SEC;- std::cout << "time taken: " << duration << " seconds" << std::endl;- - // open the target file- File targetFile( fopen( targetFileName.c_str(), "wb" ) );- if( !targetFile.IsValid() )- {- std::ostringstream oss;- oss << "failed to open \"" << sourceFileName << "\" for writing";- throw Error( oss.str() );- }- - // write the header- fwrite( &width, sizeof( int ), 1, targetFile.Get() );- fwrite( &height, sizeof( int ), 1, targetFile.Get() );- - // write the data- fwrite( targetData.Get(), 1, targetDataSize, targetFile.Get() ); }-static void Decompress( std::string const& sourceFileName, std::string const& targetFileName, int flags )+void Image::SavePng( std::string const& fileName ) const {- // open the source file- File sourceFile( fopen( sourceFileName.c_str(), "rb" ) );- if( !sourceFile.IsValid() )- {- std::ostringstream oss;- oss << "failed to open \"" << sourceFileName << "\" for reading";- throw Error( oss.str() );- }- - // get the width and height- int width, height;- fread( &width, sizeof( int ), 1, sourceFile.Get() ); - fread( &height, sizeof( int ), 1, sourceFile.Get() );- - // work out the data size- int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;- int sourceDataSize = bytesPerBlock*width*height/16;- Mem sourceData( sourceDataSize );- - // read the source data- fread( sourceData.Get(), 1, sourceDataSize, sourceFile.Get() );- // create the target rows- PngRows targetRows( width, height, 4 );- - // loop over blocks and compress them- u8 const* sourceBlock = sourceData.Get();- for( int y = 0; y < height; y += 4 )+ int const pixelSize = ( m_isColour ? 3 : 1 ) + ( m_isAlpha ? 1 : 0 );+ PngRows rows( m_width*pixelSize, m_height );++ // fill the rows with pixel data+ u8 const *src = m_pixels.Get();+ for( int y = 0; y < m_height; ++y ) {- // process a row of blocks- for( int x = 0; x < width; x += 4 )+ u8 *dest = rows[y];+ for( int x = 0; x < m_width; ++x ) {- // decompress back- u8 targetRgba[16*4];- Decompress( targetRgba, sourceBlock, flags );- - // write the data into the target rows- for( int py = 0, i = 0; py < 4; ++py )+ if( m_isColour ) {- u8* row = ( u8* )targetRows.Get()[y + py] + x*4;- for( int px = 0; px < 4; ++px, ++i )- { - for( int j = 0; j < 4; ++j )- *row++ = targetRgba[4*i + j];- }+ dest[0] = src[0];+ dest[1] = src[1];+ dest[2] = src[2];+ dest += 3; }+ else+ *dest++ = src[1];- // advance- sourceBlock += bytesPerBlock;+ if( m_isAlpha )+ *dest++ = src[3];++ src += 4; } }- - // create the target PNG- PngWriteStruct targetPng; // set up the image+ PngWriteStruct png; png_set_IHDR(- targetPng.GetPng(), targetPng.GetInfo(), width, height,- 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,- PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT + png.GetPng(), png.GetInfo(), m_width, m_height,+ 8, ( m_isColour ? PNG_COLOR_MASK_COLOR : 0) | ( m_isAlpha ? PNG_COLOR_MASK_ALPHA : 0 ), + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); // open the target file- File targetFile( fopen( targetFileName.c_str(), "wb" ) );- if( !targetFile.IsValid() )+ File file( fopen( fileName.c_str(), "wb" ) );+ if( !file.IsValid() ) { std::ostringstream oss;- oss << "failed to open \"" << targetFileName << "\" for writing";+ oss << "failed to open \"" << fileName << "\" for writing"; throw Error( oss.str() ); } // write the image- png_set_rows( targetPng.GetPng(), targetPng.GetInfo(), targetRows.Get() );- png_init_io( targetPng.GetPng(), targetFile.Get() );- png_write_png( targetPng.GetPng(), targetPng.GetInfo(), PNG_TRANSFORM_IDENTITY, 0 );+ png_set_rows( png.GetPng(), png.GetInfo(), rows.Get() );+ png_init_io( png.GetPng(), file.Get() );+ png_write_png( png.GetPng(), png.GetInfo(), PNG_TRANSFORM_IDENTITY, 0 ); }-static void Diff( std::string const& sourceFileName, std::string const& targetFileName )+void Image::Decompress( DxtData const& dxt ) {- // load the images- PngImage sourceImage( sourceFileName );- PngImage targetImage( targetFileName );- - // get the image info- int width = sourceImage.GetWidth();- int height = sourceImage.GetHeight();- int sourceStride = sourceImage.GetStride();- int targetStride = targetImage.GetStride();- int stride = std::min( sourceStride, targetStride );-- // check they match- if( width != targetImage.GetWidth() || height != targetImage.GetHeight() )- throw Error( "source and target dimensions do not match" );- - // work out the error- double error = 0.0;- for( int y = 0; y < height; ++y )- {- u8 const* sourceRow = sourceImage.GetRow( y );- u8 const* targetRow = targetImage.GetRow( y );- for( int x = 0; x < width; ++x )- { - u8 const* sourcePixel = sourceRow + x*sourceStride;- u8 const* targetPixel = targetRow + x*targetStride;- for( int i = 0; i < stride; ++i )- {- int diff = ( int )sourcePixel[i] - ( int )targetPixel[i];- error += ( double )( diff*diff );- }- }- }- error = std::sqrt( error / ( width*height ) );- - // print it out- std::cout << "rms error: " << error << std::endl;+ // allocate storage+ m_width = dxt.width;+ m_height = dxt.height;+ m_isColour = dxt.isColour;+ m_isAlpha = dxt.isAlpha;+ m_pixels.Reset( 4*m_width*m_height );++ // use the whole image decompression function to do the work+ DecompressImage( m_pixels.Get(), m_width, m_height, dxt.data.Get(), dxt.format ); }-enum Mode+void Image::Compress( DxtData& dxt, int flags ) const {- kCompress, - kDecompress,- kDiff-};+ // work out how much memory we need+ int storageSize = GetStorageRequirements( m_width, m_height, flags );++ // set the structure fields and allocate it+ dxt.width = m_width;+ dxt.height = m_height;+ dxt.format = flags & ( kDxt1 | kDxt3 | kDxt5 );+ dxt.isColour = m_isColour;+ dxt.isAlpha = m_isAlpha;+ dxt.data.Reset( storageSize );++ // use the whole image compression function to do the work+ CompressImage( m_pixels.Get(), m_width, m_height, dxt.data.Get(), flags );+}++double Image::GetRmsError( Image const& image ) const+{+ if( m_width != image.m_width || m_height != image.m_height )+ throw Error( "image dimensions mismatch when computing RMS error" );++ // accumulate colour error+ double difference = 0;+ u8 const *a = m_pixels.Get();+ u8 const *b = image.m_pixels.Get();+ for( int y = 0; y < m_height; ++y )+ {+ for( int x = 0; x < m_width; ++x )+ {+ int d0 = ( int )a[0] - ( int )b[0];+ int d1 = ( int )a[1] - ( int )b[1];+ int d2 = ( int )a[2] - ( int )b[2];+ difference += ( double )( d0*d0 + d1*d1 + d2*d2 ); + a += 4;+ b += 4;+ }+ }+ return std::sqrt( difference/( double )( m_width*m_height ) );+} int main( int argc, char* argv[] ) {@@ -495,13 +448,12 @@ // parse the command-line std::string sourceFileName; std::string targetFileName;- Mode mode = kCompress;- int method = kDxt1;- int metric = kColourMetricPerceptual;+ int format = kDxt1; int fit = kColourClusterFit; int extra = 0; bool help = false; bool arguments = true;+ bool error = false; for( int i = 1; i < argc; ++i ) { // check for options@@ -513,20 +465,16 @@ switch( word[j] ) { case 'h': help = true; break;- case 'c': mode = kCompress; break;- case 'd': mode = kDecompress; break;- case 'e': mode = kDiff; break;- case '1': method = kDxt1; break;- case '3': method = kDxt3; break;- case '5': method = kDxt5; break;- case 'u': metric = kColourMetricUniform; break;+ case '1': format = kDxt1; break;+ case '3': format = kDxt3; break;+ case '5': format = kDxt5; break; case 'r': fit = kColourRangeFit; break; case 'i': fit = kColourIterativeClusterFit; break; case 'w': extra = kWeightColourByAlpha; break; case '-': arguments = false; break; default:- std::cerr << "unknown option '" << word[j] << "'" << std::endl;- return -1;+ std::cerr << "squishpng error: unknown option '" << word[j] << "'" << std::endl;+ error = true; } } }@@ -538,60 +486,53 @@ targetFileName.assign( word ); else {- std::cerr << "unexpected argument \"" << word << "\"" << std::endl;+ std::cerr << "squishpng error: unexpected argument \"" << word << "\"" << std::endl;+ error = true; } } } // check arguments- if( help )+ if( sourceFileName.empty() )+ {+ std::cerr << "squishpng error: no source file given" << std::endl;+ error = true;+ }+ if( help || error ) { std::cout << "SYNTAX" << std::endl- << "\tsquishpng [-cde135] <source> <target>" << std::endl + << "\tsquishpng [-135riw] <source> [<target>]" << std::endl << "OPTIONS" << std::endl- << "\t-c\tCompress source png to target raw dxt (default)" << std::endl+ << "\t-h\tPrint this help message" << std::endl << "\t-135\tSpecifies whether to use DXT1 (default), DXT3 or DXT5 compression" << std::endl- << "\t-u\tUse a uniform colour metric during colour compression" << std::endl << "\t-r\tUse the fast but inferior range-based colour compressor" << std::endl << "\t-i\tUse the very slow but slightly better iterative colour compressor" << std::endl << "\t-w\tWeight colour values by alpha in the cluster colour compressor" << std::endl- << "\t-d\tDecompress source raw dxt to target png" << std::endl- << "\t-e\tDiff source and target png" << std::endl ;- return 0;- }- if( sourceFileName.empty() )- {- std::cerr << "no source file given" << std::endl;- return -1;- }- if( targetFileName.empty() )- {- std::cerr << "no target file given" << std::endl;- return -1;+ return error ? -1 : 0; }- // do the work- switch( mode )- {- case kCompress:- Compress( sourceFileName, targetFileName, method | metric | fit | extra );- break;- - case kDecompress:- Decompress( sourceFileName, targetFileName, method );- break;- - case kDiff:- Diff( sourceFileName, targetFileName );- break;- - default:- std::cerr << "unknown mode" << std::endl;- throw std::exception();- }+ // load the source image+ Image sourceImage;+ sourceImage.LoadPng( sourceFileName );++ // compress to DXT+ DxtData dxt;+ sourceImage.Compress( dxt, format | fit | extra );++ // decompress back+ Image targetImage;+ targetImage.Decompress( dxt );++ // compare the images+ double rmsError = sourceImage.GetRmsError( targetImage );+ std::cout << sourceFileName << " " << rmsError << std::endl;++ // save the target image if necessary+ if( !targetFileName.empty() )+ targetImage.SavePng( targetFileName ); } catch( std::exception& excuse ) {diff -ru squish-1.10/extra/squishtest.cpp libsquish/extra/squishtest.cpp--- squish-1.10/extra/squishtest.cpp 2006-03-03 15:20:22.000000000 -0500+++ libsquish/extra/squishtest.cpp 2009-04-01 17:31:50.000000000 -0400@@ -35,6 +35,7 @@ #include <iostream> #include <cmath> #include <cfloat>+#include <cstdlib> using namespace squish;Only in libsquish/: GNUmakefileOnly in libsquish/: imagesOnly in squish-1.10: Makefilediff -ru squish-1.10/maths.cpp libsquish/maths.cpp--- squish-1.10/maths.cpp 2006-01-19 16:10:49.000000000 -0500+++ libsquish/maths.cpp 2009-04-03 17:31:42.000000000 -0400@@ -30,6 +30,7 @@ */ #include "maths.h"+#include "simd.h" #include <cfloat> namespace squish {@@ -44,7 +45,8 @@ total += weights[i]; centroid += weights[i]*points[i]; }- centroid /= total;+ if( total > FLT_EPSILON )+ centroid /= total; // accumulate the covariance matrix Sym3x3 covariance( 0.0f );@@ -65,6 +67,8 @@ return covariance; }+#if 0+ static Vec3 GetMultiplicity1Evector( Sym3x3 const& matrix, float evalue ) { // compute M@@ -224,4 +228,32 @@ } }+#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 squishdiff -ru squish-1.10/rangefit.cpp libsquish/rangefit.cpp--- squish-1.10/rangefit.cpp 2006-09-02 04:48:17.000000000 -0400+++ libsquish/rangefit.cpp 2008-07-13 06:31:21.000000000 -0400@@ -30,15 +30,14 @@ namespace squish {-RangeFit::RangeFit( ColourSet const* colours, int flags ) +RangeFit::RangeFit( ColourSet const* colours, int flags, float* metric ) : ColourFit( colours, flags ) {- // initialise the metric- bool perceptual = ( ( m_flags & kColourMetricPerceptual ) != 0 );- if( perceptual )- m_metric = Vec3( 0.2126f, 0.7152f, 0.0722f );+ // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)+ if( metric )+ m_metric = Vec3( metric[0], metric[1], metric[2] ); else- m_metric = Vec3( 1.0f );+ m_metric = Vec3( 1.0f ); // initialise the best error m_besterror = FLT_MAX;diff -ru squish-1.10/rangefit.h libsquish/rangefit.h--- squish-1.10/rangefit.h 2006-07-25 10:28:13.000000000 -0400+++ libsquish/rangefit.h 2008-07-13 06:31:21.000000000 -0400@@ -37,7 +37,7 @@ class RangeFit : public ColourFit { public:- RangeFit( ColourSet const* colours, int flags );+ RangeFit( ColourSet const* colours, int flags, float* metric ); private: virtual void Compress3( void* block );diff -ru squish-1.10/simd_ve.h libsquish/simd_ve.h--- squish-1.10/simd_ve.h 2007-03-21 15:31:51.000000000 -0400+++ libsquish/simd_ve.h 2008-07-13 06:31:21.000000000 -0400@@ -31,7 +31,7 @@ namespace squish {-#define VEC4_CONST( X ) Vec4( ( vector float )( X ) )+#define VEC4_CONST( X ) Vec4( ( vector float ){ X } ) class Vec4 {@@ -96,7 +96,7 @@ Vec4& operator*=( Arg v ) {- m_v = vec_madd( m_v, v.m_v, ( vector float )( -0.0f ) );+ m_v = vec_madd( m_v, v.m_v, ( vector float ){ -0.0f } ); return *this; }@@ -112,7 +112,7 @@ friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right ) {- return Vec4( vec_madd( left.m_v, right.m_v, ( vector float )( -0.0f ) ) );+ return Vec4( vec_madd( left.m_v, right.m_v, ( vector float ){ -0.0f } ) ); } //! Returns a*b + c@@ -133,7 +133,7 @@ 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 ) );+ vector float diff = vec_nmsub( estimate, v.m_v, ( vector float ){ 1.0f } ); return Vec4( vec_madd( diff, estimate, estimate ) ); }diff -ru squish-1.10/squish.cpp libsquish/squish.cpp--- squish-1.10/squish.cpp 2007-03-21 15:31:51.000000000 -0400+++ libsquish/squish.cpp 2008-07-13 06:31:21.000000000 -0400@@ -39,28 +39,19 @@ // grab the flag bits int method = flags & ( kDxt1 | kDxt3 | kDxt5 ); int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit );- int metric = flags & ( kColourMetricPerceptual | kColourMetricUniform ); int extra = flags & kWeightColourByAlpha; // set defaults if( method != kDxt3 && method != kDxt5 ) method = kDxt1;- if( fit != kColourRangeFit )+ if( fit != kColourRangeFit && fit != kColourIterativeClusterFit ) fit = kColourClusterFit;- if( metric != kColourMetricUniform )- metric = kColourMetricPerceptual; // done- return method | fit | metric | extra;+ return method | fit | extra; }-void Compress( u8 const* rgba, void* block, int flags )-{- // compress with full mask- CompressMasked( rgba, 0xffff, block, flags );-}--void CompressMasked( u8 const* rgba, int mask, void* block, int flags )+void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric ) { // fix any bad flags flags = FixFlags( flags );@@ -84,13 +75,13 @@ else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 ) { // do a range fit- RangeFit fit( &colours, flags );+ RangeFit fit( &colours, flags, metric ); fit.Compress( colourBlock ); } else { // default to a cluster fit (could be iterative or not)- ClusterFit fit( &colours, flags );+ ClusterFit fit( &colours, flags, metric ); fit.Compress( colourBlock ); }@@ -133,7 +124,7 @@ return blockcount*blocksize; }-void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags )+void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric ) { // fix any bad flags flags = FixFlags( flags );@@ -179,7 +170,7 @@ } // compress it into the output- CompressMasked( sourceRgba, mask, targetBlock, flags );+ CompressMasked( sourceRgba, mask, targetBlock, flags, metric ); // advance targetBlock += bytesPerBlock;diff -ru squish-1.10/squish.h libsquish/squish.h--- squish-1.10/squish.h 2007-03-21 16:13:51.000000000 -0400+++ libsquish/squish.h 2008-07-13 06:31:21.000000000 -0400@@ -56,12 +56,6 @@ //! 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.- kColourMetricUniform = ( 1 << 6 ),- //! Weight the colour by alpha during cluster fit (disabled by default). kWeightColourByAlpha = ( 1 << 7 ) };@@ -71,74 +65,90 @@ /*! @brief Compresses a 4x4 block of 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 flags Compression flags.+ @param metric An optional perceptual metric. 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: { r1, g1, b1, a1, .... , r16, g16, b16, a16 }++ The mask parameter enables only certain pixels within the block. The lowest+ 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+ arbitrary colours in the output block. An example of how this can be used+ 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 should specify either kDxt1, kDxt3 or kDxt5 compression, 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. DXT3 and DXT5 compression require 16 bytes of storage per block.- The flags parameter can also specify a preferred colour compressor and - colour error metric to use when fitting the RGB components of the data. - Possible colour compressors are: kColourClusterFit (the default), - kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics - are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no - flags are specified in any particular category then the default will be - used. Unknown flags are ignored.- - When using kColourClusterFit, an additional flag can be specified to- weight the colour of each pixel by its alpha value. For images that are- rendered using alpha blending, this can significantly increase the - perceived quality.+ The flags parameter can also specify a preferred colour compressor to use + when fitting the RGB components of the data. Possible colour compressors + 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. @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 flags Compression flags.+ @param metric An optional perceptual metric. 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: { r1, g1, b1, a1, .... , r16, g16, b16, a16 }- - The mask parameter enables only certain pixels within the block. The lowest- 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- arbitrary colours in the output block. An example of how this can be used- 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 should specify either kDxt1, kDxt3 or kDxt5 compression, 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. DXT3 and DXT5 compression require 16 bytes of storage per block.- The flags parameter can also specify a preferred colour compressor and - colour error metric to use when fitting the RGB components of the data. - Possible colour compressors are: kColourClusterFit (the default), - kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics - are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no - flags are specified in any particular category then the default will be - used. Unknown flags are ignored.- - When using kColourClusterFit, an additional flag can be specified to- weight the colour of each pixel by its alpha value. For images that are- rendered using alpha blending, this can significantly increase the - perceived quality.-*/-void CompressMasked( u8 const* rgba, int mask, void* block, int flags );+ The flags parameter can also specify a preferred colour compressor to use + when fitting the RGB components of the data. Possible colour compressors + 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.++ This method is an inline that calls CompressMasked with a mask of 0xffff, + provided for compatibility with older versions of squish.+*/+inline void Compress( u8 const* rgba, void* block, int flags, float* metric = 0 )+{+ CompressMasked( rgba, 0xffff, block, flags, metric );+} // -----------------------------------------------------------------------------@@ -186,6 +196,7 @@ @param height The height of the source image. @param blocks Storage for the compressed output. @param flags Compression flags.+ @param metric An optional perceptual metric. The source pixels should be presented as a contiguous array of width*height rgba values, with each component as 1 byte each. In memory this should be:@@ -197,24 +208,29 @@ compression, 8 bytes of storage are required for each compressed DXT block. DXT3 and DXT5 compression require 16 bytes of storage per block.- The flags parameter can also specify a preferred colour compressor and - colour error metric to use when fitting the RGB components of the data. - Possible colour compressors are: kColourClusterFit (the default), - kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics - are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no - flags are specified in any particular category then the default will be - used. Unknown flags are ignored.- - When using kColourClusterFit, an additional flag can be specified to- weight the colour of each pixel by its alpha value. For images that are- rendered using alpha blending, this can significantly increase the - perceived quality.- - Internally this function calls squish::Compress for each block. To see how- much memory is required in the compressed image, use- squish::GetStorageRequirements.+ The flags parameter can also specify a preferred colour compressor to use + when fitting the RGB components of the data. Possible colour compressors + 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.++ 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, void* blocks, int flags, float* metric = 0 ); // -----------------------------------------------------------------------------Only in squish-1.10: squish-Info.plistOnly in squish-1.10: squish.xcodeprojOnly in libsquish/: testOnly in squish-1.10: texture_compression_s3tc.txtOnly in squish-1.10/vs7: squish.sln