Lightweight Commit of libsquish

master
Jack Andersen 9 years ago
commit 592d3b8831
  1. 104
      CMakeLists.txt
  2. 14
      CMakeModules/FindlibSquish.cmake
  3. 52
      ChangeLog
  4. 20
      LICENSE
  5. 35
      README
  6. 350
      alpha.cpp
  7. 41
      alpha.h
  8. 392
      clusterfit.cpp
  9. 61
      clusterfit.h
  10. 214
      colourblock.cpp
  11. 41
      colourblock.h
  12. 54
      colourfit.cpp
  13. 56
      colourfit.h
  14. 121
      colourset.cpp
  15. 58
      colourset.h
  16. 49
      config.h
  17. 259
      maths.cpp
  18. 233
      maths.h
  19. 201
      rangefit.cpp
  20. 54
      rangefit.h
  21. 40
      simd.h
  22. 183
      simd_float.h
  23. 180
      simd_sse.h
  24. 166
      simd_ve.h
  25. 172
      singlecolourfit.cpp
  26. 58
      singlecolourfit.h
  27. 230
      squish.cpp
  28. 263
      squish.h

@ -0,0 +1,104 @@
# cmake build file for squish
# by Stefan Roettger (stefan@stereofx.org)
# updated by Simon Brown (si@sjbrown.co.uk)
# features:
# Xcode: builds universal binaries, uses SSE2 on i386 and Altivec on ppc
# Unix and VS: SSE2 support is enabled by default
# use BUILD_SQUISH_WITH_SSE2 and BUILD_SQUISH_WITH_ALTIVEC to override
PROJECT(squish)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3)
OPTION(BUILD_SQUISH_WITH_SSE2 "Build with SSE2." ON)
OPTION(BUILD_SQUISH_WITH_ALTIVEC "Build with Altivec." OFF)
OPTION(BUILD_SHARED_LIBS "Build shared libraries." OFF)
OPTION(BUILD_SQUISH_EXTRA "Build extra source code." OFF)
IF (CMAKE_GENERATOR STREQUAL "Xcode")
SET(CMAKE_OSX_ARCHITECTURES "i386;ppc")
ELSE (CMAKE_GENERATOR STREQUAL "Xcode")
IF (BUILD_SQUISH_WITH_SSE2 AND NOT WIN32)
ADD_DEFINITIONS(-DSQUISH_USE_SSE=2 -msse2)
ENDIF (BUILD_SQUISH_WITH_SSE2 AND NOT WIN32)
IF (BUILD_SQUISH_WITH_ALTIVEC AND NOT WIN32)
ADD_DEFINITIONS(-DSQUISH_USE_ALTIVEC=1 -maltivec)
ENDIF (BUILD_SQUISH_WITH_ALTIVEC AND NOT WIN32)
ENDIF (CMAKE_GENERATOR STREQUAL "Xcode")
SET(SQUISH_HDRS
squish.h
)
SET(SQUISH_SRCS
alpha.cpp
alpha.h
clusterfit.cpp
clusterfit.h
colourblock.cpp
colourblock.h
colourfit.cpp
colourfit.h
colourset.cpp
colourset.h
maths.cpp
maths.h
rangefit.cpp
rangefit.h
simd.h
simd_float.h
simd_sse.h
simd_ve.h
singlecolourfit.cpp
singlecolourfit.h
singlecolourlookup.inl
squish.cpp
)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
ADD_LIBRARY(squish ${SQUISH_SRCS} ${SQUISH_HDRS})
SET_TARGET_PROPERTIES(
squish PROPERTIES
PUBLIC_HEADER "${SQUISH_HDRS}"
VERSION 0.0
SOVERSION 0.0
DEBUG_POSTFIX "d"
XCODE_ATTRIBUTE_GCC_PREPROCESSOR_DEFINITIONS "$(SQUISH_CPP_$(CURRENT_ARCH))"
XCODE_ATTRIBUTE_OTHER_CFLAGS "$(SQUISH_CFLAGS_$(CURRENT_ARCH))"
XCODE_ATTRIBUTE_SQUISH_CPP_i386 "SQUISH_USE_SSE=2"
XCODE_ATTRIBUTE_SQUISH_CFLAGS_i386 ""
XCODE_ATTRIBUTE_SQUISH_CPP_ppc "SQUISH_USE_ALTIVEC=1"
XCODE_ATTRIBUTE_SQUISH_CFLAGS_ppc "-maltivec"
)
IF (BUILD_SQUISH_EXTRA)
SET(SQUISHTEST_SRCS extra/squishtest.cpp)
ADD_EXECUTABLE(squishtest ${SQUISHTEST_SRCS})
SET_TARGET_PROPERTIES(squishtest PROPERTIES DEBUG_POSTFIX "d")
TARGET_LINK_LIBRARIES(squishtest squish)
SET(SQUISHPNG_SRCS extra/squishpng.cpp)
FIND_PACKAGE(PNG)
IF (PNG_FOUND)
SET(CMAKE_PLATFORM_IMPLICIT_INCLUDE_DIRECTORIES)
INCLUDE_DIRECTORIES(${PNG_INCLUDE_DIR})
ADD_EXECUTABLE(squishpng ${SQUISHPNG_SRCS})
SET_TARGET_PROPERTIES(squishpng PROPERTIES DEBUG_POSTFIX "d")
TARGET_LINK_LIBRARIES(squishpng squish ${PNG_LIBRARIES})
ENDIF (PNG_FOUND)
ENDIF (BUILD_SQUISH_EXTRA)
INSTALL(
TARGETS squish
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
PUBLIC_HEADER DESTINATION include
)

@ -0,0 +1,14 @@
# Defines
# LIBSQUISH_FOUND
# LIBSQUISH_INCLUDE_DIR
# LIBSQUISH_LIBRARIES
FIND_PATH(LIBSQUISH_INCLUDE_DIR squish.h PATHS . squish .. ../squish DOC "Directory containing libSquish headers")
FIND_LIBRARY(LIBSQUISH_LIBRARY NAMES squish libsquish PATHS . squish .. ../squish PATH_SUFFIXES lib lib64 release minsizerel relwithdebinfo DOC "Path to libSquish library")
SET(LIBSQUISH_LIBRARIES ${LIBSQUISH_LIBRARY})
IF (LIBSQUISH_LIBRARY AND LIBSQUISH_INCLUDE_DIR)
SET(LIBSQUISH_FOUND TRUE)
MESSAGE(STATUS "Found libSquish: ${LIBSQUISH_LIBRARY}")
ENDIF (LIBSQUISH_LIBRARY AND LIBSQUISH_INCLUDE_DIR)

@ -0,0 +1,52 @@
1.10
* Iterative cluster fit is now considered to be a new compression mode
* The core cluster fit is now 4x faster using contributions by Ignacio
Castano from NVIDIA
* The single colour lookup table has been halved by exploiting symmetry
1.9
* Added contributed SSE1 truncate implementation
* Changed use of SQUISH_USE_SSE to be 1 for SSE and 2 for SSE2 instructions
* Cluster fit is now iterative to further reduce image error
1.8
* Switched from using floor to trunc for much better SSE performance (again)
* Xcode build now expects libpng in /usr/local for extra/squishpng
1.7
* Fixed floating-point equality issue in clusterfit sort (x86 affected only)
* Implemented proper SSE(2) floor function for 50% speedup on SSE builds
* The range fit implementation now uses the correct colour metric
1.6
* Fixed bug in CompressImage where masked pixels were not skipped over
* DXT3 and DXT5 alpha compression now properly use the mask to ignore pixels
* Fixed major DXT1 bug that can generate unexpected transparent pixels
1.5
* Added CompressMasked function to handle incomplete DXT blocks more cleanly
* Added kWeightColourByAlpha flag for better quality images when alpha blending
1.4
* Fixed stack overflow in rangefit
1.3
* Worked around SSE floor implementation bug, proper fix needed!
* This release has visual studio and makefile builds that work
1.2
* Added provably optimal single colour compressor
* Added extra/squishgen.cpp that generates single colour lookup tables
1.1
* Fixed a DXT1 colour output bug
* Changed argument order for Decompress function to match Compress
* Added GetStorageRequirements function
* Added CompressImage function
* Added DecompressImage function
* Moved squishtool.cpp to extra/squishpng.cpp
* Added extra/squishtest.cpp
1.0
* Initial release

@ -0,0 +1,20 @@
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
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.

@ -0,0 +1,35 @@
LICENSE
-------
The squish library is distributed under the terms and conditions of the MIT
license. This license is specified at the top of each source file and must be
preserved in its entirety.
BUILDING AND INSTALLING THE LIBRARY
-----------------------------------
If you are using Visual Studio 2003 or above under Windows then load the Visual
Studio 2003 project in the vs7 folder. By default, the library is built using
SSE2 optimisations. To change this either change or remove the SQUISH_USE_SSE=2
from the preprocessor symbols.
If you are using a Mac then load the Xcode 2.2 project in the distribution. By
default, the library is built using Altivec optimisations. To change this
either change or remove SQUISH_USE_ALTIVEC=1 from the preprocessor symbols. I
guess I'll have to think about changing this for the new Intel Macs that are
rolling out...
If you are using unix then first edit the config file in the base directory of
the distribution, enabling Altivec or SSE with the USE_ALTIVEC or USE_SSE
variables, and editing the optimisation flags passed to the C++ compiler if
necessary. Then make can be used to build the library, and make install (from
the superuser account) can be used to install (into /usr/local by default).
REPORTING BUGS OR FEATURE REQUESTS
----------------------------------
Feedback can be sent to Simon Brown (the developer) at si@sjbrown.co.uk
New releases are announced on the squish library homepage at
http://sjbrown.co.uk/?code=squish

@ -0,0 +1,350 @@
/* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
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.
-------------------------------------------------------------------------- */
#include "alpha.h"
#include <climits>
#include <algorithm>
namespace squish {
static int FloatToInt( float a, int limit )
{
// use ANSI round-to-zero behaviour to get round-to-nearest
int i = ( int )( a + 0.5f );
// clamp to the limit
if( i < 0 )
i = 0;
else if( i > limit )
i = limit;
// done
return i;
}
void CompressAlphaDxt3( u8 const* rgba, int mask, void* 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
bytes[i] = ( u8 )( quant1 | ( quant2 << 4 ) );
}
}
void DecompressAlphaDxt3( u8* rgba, void 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
rgba[8*i + 3] = lo | ( lo << 4 );
rgba[8*i + 7] = hi | ( hi >> 4 );
}
}
static void FixRange( int& min, int& max, int steps )
{
if( max - min < steps )
max = std::min( min + steps, 255 );
if( max - min < steps )
min = std::max( 0, max - steps );
}
static int FitCodes( u8 const* rgba, int mask, u8 const* codes, u8* indices )
{
// fit each alpha value to the codebook
int err = 0;
for( int i = 0; i < 16; ++i )
{
// check this pixel is valid
int bit = 1 << i;
if( ( mask & bit ) == 0 )
{
// use the first code
indices[i] = 0;
continue;
}
// find the least error and corresponding index
int value = rgba[4*i + 3];
int least = INT_MAX;
int index = 0;
for( int j = 0; j < 8; ++j )
{
// get the squared error from this code
int dist = ( int )value - ( int )codes[j];
dist *= dist;
// compare with the best so far
if( dist < least )
{
least = dist;
index = j;
}
}
// save this index and accumulate the error
indices[i] = ( u8 )index;
err += least;
}
// return the total error
return err;
}
static void WriteAlphaBlock( int alpha0, int alpha1, u8 const* indices, void* block )
{
u8* bytes = reinterpret_cast< u8* >( block );
// write the first two bytes
bytes[0] = ( u8 )alpha0;
bytes[1] = ( u8 )alpha1;
// pack the indices with 3 bits each
u8* dest = bytes + 2;
u8 const* src = indices;
for( int i = 0; i < 2; ++i )
{
// pack 8 3-bit values
int value = 0;
for( int j = 0; j < 8; ++j )
{
int index = *src++;
value |= ( index << 3*j );
}
// store in 3 bytes
for( int j = 0; j < 3; ++j )
{
int byte = ( value >> 8*j ) & 0xff;
*dest++ = ( u8 )byte;
}
}
}
static void WriteAlphaBlock5( int alpha0, int alpha1, u8 const* indices, void* block )
{
// check the relative values of the endpoints
if( alpha0 > alpha1 )
{
// swap the indices
u8 swapped[16];
for( int i = 0; i < 16; ++i )
{
u8 index = indices[i];
if( index == 0 )
swapped[i] = 1;
else if( index == 1 )
swapped[i] = 0;
else if( index <= 5 )
swapped[i] = 7 - index;
else
swapped[i] = index;
}
// write the block
WriteAlphaBlock( alpha1, alpha0, swapped, block );
}
else
{
// write the block
WriteAlphaBlock( alpha0, alpha1, indices, block );
}
}
static void WriteAlphaBlock7( int alpha0, int alpha1, u8 const* indices, void* block )
{
// check the relative values of the endpoints
if( alpha0 < alpha1 )
{
// swap the indices
u8 swapped[16];
for( int i = 0; i < 16; ++i )
{
u8 index = indices[i];
if( index == 0 )
swapped[i] = 1;
else if( index == 1 )
swapped[i] = 0;
else
swapped[i] = 9 - index;
}
// write the block
WriteAlphaBlock( alpha1, alpha0, swapped, block );
}
else
{
// write the block
WriteAlphaBlock( alpha0, alpha1, indices, block );
}
}
void CompressAlphaDxt5( u8 const* rgba, int mask, void* block )
{
// get the range for 5-alpha and 7-alpha interpolation
int min5 = 255;
int max5 = 0;
int min7 = 255;
int max7 = 0;
for( int i = 0; i < 16; ++i )
{
// check this pixel is valid
int bit = 1 << i;
if( ( mask & bit ) == 0 )
continue;
// incorporate into the min/max
int value = rgba[4*i + 3];
if( value < min7 )
min7 = value;
if( value > max7 )
max7 = value;
if( value != 0 && value < min5 )
min5 = value;
if( value != 255 && value > max5 )
max5 = value;
}
// handle the case that no valid range was found
if( min5 > max5 )
min5 = max5;
if( min7 > max7 )
min7 = max7;
// fix the range to be the minimum in each case
FixRange( min5, max5, 5 );
FixRange( min7, max7, 7 );
// set up the 5-alpha code book
u8 codes5[8];
codes5[0] = ( u8 )min5;
codes5[1] = ( u8 )max5;
for( int i = 1; i < 5; ++i )
codes5[1 + i] = ( u8 )( ( ( 5 - i )*min5 + i*max5 )/5 );
codes5[6] = 0;
codes5[7] = 255;
// set up the 7-alpha code book
u8 codes7[8];
codes7[0] = ( u8 )min7;
codes7[1] = ( u8 )max7;
for( int i = 1; i < 7; ++i )
codes7[1 + i] = ( u8 )( ( ( 7 - i )*min7 + i*max7 )/7 );
// fit the data to both code books
u8 indices5[16];
u8 indices7[16];
int err5 = FitCodes( rgba, mask, codes5, indices5 );
int err7 = FitCodes( rgba, mask, codes7, indices7 );
// save the block with least error
if( err5 <= err7 )
WriteAlphaBlock5( min5, max5, indices5, block );
else
WriteAlphaBlock7( min7, max7, indices7, block );
}
void DecompressAlphaDxt5( u8* rgba, void const* block )
{
// get the two alpha values
u8 const* bytes = reinterpret_cast< u8 const* >( block );
int alpha0 = bytes[0];
int alpha1 = bytes[1];
// compare the values to build the codebook
u8 codes[8];
codes[0] = ( u8 )alpha0;
codes[1] = ( u8 )alpha1;
if( alpha0 <= alpha1 )
{
// use 5-alpha codebook
for( int i = 1; i < 5; ++i )
codes[1 + i] = ( u8 )( ( ( 5 - i )*alpha0 + i*alpha1 )/5 );
codes[6] = 0;
codes[7] = 255;
}
else
{
// use 7-alpha codebook
for( int i = 1; i < 7; ++i )
codes[1 + i] = ( u8 )( ( ( 7 - i )*alpha0 + i*alpha1 )/7 );
}
// decode the indices
u8 indices[16];
u8 const* src = bytes + 2;
u8* dest = indices;
for( int i = 0; i < 2; ++i )
{
// grab 3 bytes
int value = 0;
for( int j = 0; j < 3; ++j )
{
int byte = *src++;
value |= ( byte << 8*j );
}
// unpack 8 3-bit values from it
for( int j = 0; j < 8; ++j )
{
int index = ( value >> 3*j ) & 0x7;
*dest++ = ( u8 )index;
}
}
// write out the indexed codebook values
for( int i = 0; i < 16; ++i )
rgba[4*i + 3] = codes[indices[i]];
}
} // namespace squish

@ -0,0 +1,41 @@
/* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
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.
-------------------------------------------------------------------------- */
#ifndef SQUISH_ALPHA_H
#define SQUISH_ALPHA_H
#include "squish.h"
namespace squish {
void CompressAlphaDxt3( u8 const* rgba, int mask, void* block );
void CompressAlphaDxt5( u8 const* rgba, int mask, void* block );
void DecompressAlphaDxt3( u8* rgba, void const* block );
void DecompressAlphaDxt5( u8* rgba, void const* block );
} // namespace squish
#endif // ndef SQUISH_ALPHA_H

@ -0,0 +1,392 @@
/* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Copyright (c) 2007 Ignacio Castano icastano@nvidia.com
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
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.
-------------------------------------------------------------------------- */
#include "clusterfit.h"
#include "colourset.h"
#include "colourblock.h"
#include <cfloat>
namespace squish {
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 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();
// get the covariance matrix
Sym3x3 covariance = ComputeWeightedCovariance( count, values, m_colours->GetWeights() );
// compute the principle component
m_principle = ComputePrincipleComponent( covariance );
}
bool ClusterFit::ConstructOrdering( Vec3 const& axis, int iteration )
{
// cache some values
int const count = m_colours->GetCount();
Vec3 const* values = m_colours->GetPoints();
// build the list of dot products
float dps[16];
u8* order = ( u8* )m_order + 16*iteration;
for( int i = 0; i < count; ++i )
{
dps[i] = Dot( values[i], axis );
order[i] = ( u8 )i;
}
// stable sort using them
for( int i = 0; i < count; ++i )
{
for( int j = i; j > 0 && dps[j] < dps[j - 1]; --j )
{
std::swap( dps[j], dps[j - 1] );
std::swap( order[j], order[j - 1] );
}
}
// check this ordering is unique
for( int it = 0; it < iteration; ++it )
{
u8 const* prev = ( u8* )m_order + 16*it;
bool same = true;
for( int i = 0; i < count; ++i )
{
if( order[i] != prev[i] )
{
same = false;
break;
}
}
if( same )
return false;
}
// copy the ordering and weight all the points
Vec3 const* unweighted = m_colours->GetPoints();
float const* weights = m_colours->GetWeights();
m_xsum_wsum = VEC4_CONST( 0.0f );
for( int i = 0; i < count; ++i )
{
int j = order[i];
Vec4 p( unweighted[j].X(), unweighted[j].Y(), unweighted[j].Z(), 1.0f );
Vec4 w( weights[j] );
Vec4 x = p*w;
m_points_weights[i] = x;
m_xsum_wsum += x;
}
return true;
}
void ClusterFit::Compress3( void* block )
{
// declare variables
int const count = m_colours->GetCount();
Vec4 const two = VEC4_CONST( 2.0 );
Vec4 const one = VEC4_CONST( 1.0f );
Vec4 const half_half2( 0.5f, 0.5f, 0.5f, 0.25f );
Vec4 const zero = VEC4_CONST( 0.0f );
Vec4 const half = VEC4_CONST( 0.5f );
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 );
// prepare an ordering using the principle axis
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 );
Vec4 beta2_sum = betax_sum.SplatW();
Vec4 alphabeta_sum = ( part1*half_half2 ).SplatW();
// compute the least-squares optimal points
Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) );
Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor;
Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor;
// clamp to the grid
a = Min( one, Max( zero, a ) );
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 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
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 )
{
// declare variables
int const count = m_colours->GetCount();
Vec4 const two = VEC4_CONST( 2.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 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 zero = VEC4_CONST( 0.0f );
Vec4 const half = VEC4_CONST( 0.5f );
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 );
// prepare an ordering using the principle axis
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
Vec4 const alphax_sum = MultiplyAdd( part2, onethird_onethird2, MultiplyAdd( part1, twothirds_twothirds2, part0 ) );
Vec4 const alpha2_sum = alphax_sum.SplatW();
Vec4 const betax_sum = MultiplyAdd( part1, onethird_onethird2, MultiplyAdd( part2, twothirds_twothirds2, part3 ) );
Vec4 const beta2_sum = betax_sum.SplatW();
Vec4 const alphabeta_sum = twonineths*( part1 + part2 ).SplatW();
// compute the least-squares optimal points
Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) );
Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor;
Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor;
// clamp to the grid
a = Min( one, Max( zero, a ) );
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 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;
besterror = error;
besti = i;
bestj = j;
bestk = k;
bestiteration = iterationIndex;
}
// advance
if( k == count )
break;
part2 += m_points_weights[k];
++k;
}
// 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 < 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

@ -0,0 +1,61 @@
/* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Copyright (c) 2007 Ignacio Castano icastano@nvidia.com
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
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.
-------------------------------------------------------------------------- */
#ifndef SQUISH_CLUSTERFIT_H
#define SQUISH_CLUSTERFIT_H
#include "squish.h"
#include "maths.h"
#include "simd.h"
#include "colourfit.h"
namespace squish {
class ClusterFit : public ColourFit
{
public:
ClusterFit( ColourSet const* colours, int flags, float* metric );
private:
bool ConstructOrdering( Vec3 const& axis, int iteration );
virtual void Compress3( void* block );
virtual void Compress4( void* block );
enum { kMaxIterations = 8 };
int m_iterationCount;
Vec3 m_principle;
u8 m_order[16*kMaxIterations];
Vec4 m_points_weights[16];
Vec4 m_xsum_wsum;
Vec4 m_metric;
Vec4 m_besterror;
};
} // namespace squish
#endif // ndef SQUISH_CLUSTERFIT_H

@ -0,0 +1,214 @@
/* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
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.
-------------------------------------------------------------------------- */
#include "colourblock.h"
namespace squish {
static int FloatToInt( float a, int limit )
{
// use ANSI round-to-zero behaviour to get round-to-nearest
int i = ( int )( a + 0.5f );
// clamp to the limit
if( i < 0 )
i = 0;
else if( i > limit )
i = limit;
// done
return i;
}
static int FloatTo565( Vec3::Arg colour )
{
// get the components in the correct range
int r = FloatToInt( 31.0f*colour.X(), 31 );
int g = FloatToInt( 63.0f*colour.Y(), 63 );
int b = FloatToInt( 31.0f*colour.Z(), 31 );
// pack into a single value
return ( r << 11 ) | ( g << 5 ) | b;
}
static void WriteColourBlock( int a, int b, u8* indices, void* block )
{
// get the block as bytes
u8* bytes = ( u8* )block;
// write the endpoints
bytes[0] = ( u8 )( a & 0xff );
bytes[1] = ( u8 )( a >> 8 );
bytes[2] = ( u8 )( b & 0xff );
bytes[3] = ( u8 )( b >> 8 );
// write the indices
for( int i = 0; i < 4; ++i )
{
u8 const* ind = indices + 4*i;
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 )
{
// get the packed values
int a = FloatTo565( start );
int b = FloatTo565( end );
// remap the indices
u8 remapped[16];
if( a <= b )
{
// use the indices directly
for( int i = 0; i < 16; ++i )
remapped[i] = indices[i];
}
else
{
// swap a and b
std::swap( a, b );
for( int i = 0; i < 16; ++i )
{
if( indices[i] == 0 )
remapped[i] = 1;
else if( indices[i] == 1 )
remapped[i] = 0;
else
remapped[i] = indices[i];
}
}
// write the block
WriteColourBlock( a, b, remapped, block );
}
void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block )
{
// get the packed values
int a = FloatTo565( start );
int b = FloatTo565( end );
// remap the indices
u8 remapped[16];
if( a < b )
{
// swap a and b
std::swap( a, b );
for( int i = 0; i < 16; ++i )
remapped[i] = ( indices[i] ^ 0x1 ) & 0x3;
}
else if( a == b )
{
// use index 0
for( int i = 0; i < 16; ++i )
remapped[i] = 0;
}
else
{
// use the indices directly
for( int i = 0; i < 16; ++i )
remapped[i] = indices[i];
}
// write the block
WriteColourBlock( a, b, remapped, block );
}
static int Unpack565( u8 const* packed, u8* colour )
{
// build the packed value
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
colour[0] = ( red << 3 ) | ( red >> 2 );
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 )
{
// get the block bytes
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 )
{
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

@ -0,0 +1,41 @@
/* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
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.
-------------------------------------------------------------------------- */
#ifndef SQUISH_COLOURBLOCK_H
#define SQUISH_COLOURBLOCK_H
#include "squish.h"
#include "maths.h"
namespace squish {
void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block );
void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block );
void DecompressColour( u8* rgba, void const* block, bool isDxt1 );
} // namespace squish
#endif // ndef SQUISH_COLOURBLOCK_H

@ -0,0 +1,54 @@
/* -----------------------------------------------------------------------------
Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
Permission is hereby granted, free of charge, to any person obtaining