mirror of https://github.com/AxioDL/libSquish.git
commit
592d3b8831
@ -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 |
||||