medfall

A super great game engine
Log | Files | Refs

alpha.cc (8223B)


      1 /* -----------------------------------------------------------------------------
      2 
      3 	Copyright (c) 2006 Simon Brown                          si@sjbrown.co.uk
      4 
      5 	Permission is hereby granted, free of charge, to any person obtaining
      6 	a copy of this software and associated documentation files (the 
      7 	"Software"), to	deal in the Software without restriction, including
      8 	without limitation the rights to use, copy, modify, merge, publish,
      9 	distribute, sublicense, and/or sell copies of the Software, and to 
     10 	permit persons to whom the Software is furnished to do so, subject to 
     11 	the following conditions:
     12 
     13 	The above copyright notice and this permission notice shall be included
     14 	in all copies or substantial portions of the Software.
     15 
     16 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     17 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
     18 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     19 	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
     20 	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
     21 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
     22 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23 	
     24    -------------------------------------------------------------------------- */
     25    
     26 #include "alpha.h"
     27 
     28 #include <climits>
     29 #include <algorithm>
     30 
     31 namespace squish {
     32 
     33 static int FloatToInt( float a, int limit )
     34 {
     35 	// use ANSI round-to-zero behaviour to get round-to-nearest
     36 	int i = ( int )( a + 0.5f );
     37 
     38 	// clamp to the limit
     39 	if( i < 0 )
     40 		i = 0;
     41 	else if( i > limit )
     42 		i = limit; 
     43 
     44 	// done
     45 	return i;
     46 }
     47 
     48 void CompressAlphaDxt3( u8 const* rgba, int mask, void* block )
     49 {
     50 	u8* bytes = reinterpret_cast< u8* >( block );
     51 	
     52 	// quantise and pack the alpha values pairwise
     53 	for( int i = 0; i < 8; ++i )
     54 	{
     55 		// quantise down to 4 bits
     56 		float alpha1 = ( float )rgba[8*i + 3] * ( 15.0f/255.0f );
     57 		float alpha2 = ( float )rgba[8*i + 7] * ( 15.0f/255.0f );
     58 		int quant1 = FloatToInt( alpha1, 15 );
     59 		int quant2 = FloatToInt( alpha2, 15 );
     60 		
     61 		// set alpha to zero where masked
     62 		int bit1 = 1 << ( 2*i );
     63 		int bit2 = 1 << ( 2*i + 1 );
     64 		if( ( mask & bit1 ) == 0 )
     65 			quant1 = 0;
     66 		if( ( mask & bit2 ) == 0 )
     67 			quant2 = 0;
     68 
     69 		// pack into the byte
     70 		bytes[i] = ( u8 )( quant1 | ( quant2 << 4 ) );
     71 	}
     72 }
     73 
     74 void DecompressAlphaDxt3( u8* rgba, void const* block )
     75 {
     76 	u8 const* bytes = reinterpret_cast< u8 const* >( block );
     77 	
     78 	// unpack the alpha values pairwise
     79 	for( int i = 0; i < 8; ++i )
     80 	{
     81 		// quantise down to 4 bits
     82 		u8 quant = bytes[i];
     83 		
     84 		// unpack the values
     85 		u8 lo = quant & 0x0f;
     86 		u8 hi = quant & 0xf0;
     87 
     88 		// convert back up to bytes
     89 		rgba[8*i + 3] = lo | ( lo << 4 );
     90 		rgba[8*i + 7] = hi | ( hi >> 4 );
     91 	}
     92 }
     93 
     94 static void FixRange( int& min, int& max, int steps )
     95 {
     96 	if( max - min < steps )
     97 		max = std::min( min + steps, 255 );
     98 	if( max - min < steps )
     99 		min = std::max( 0, max - steps );
    100 }
    101 
    102 static int FitCodes( u8 const* rgba, int mask, u8 const* codes, u8* indices )
    103 {
    104 	// fit each alpha value to the codebook
    105 	int err = 0;
    106 	for( int i = 0; i < 16; ++i )
    107 	{
    108 		// check this pixel is valid
    109 		int bit = 1 << i;
    110 		if( ( mask & bit ) == 0 )
    111 		{
    112 			// use the first code
    113 			indices[i] = 0;
    114 			continue;
    115 		}
    116 		
    117 		// find the least error and corresponding index
    118 		int value = rgba[4*i + 3];
    119 		int least = INT_MAX;
    120 		int index = 0;
    121 		for( int j = 0; j < 8; ++j )
    122 		{
    123 			// get the squared error from this code
    124 			int dist = ( int )value - ( int )codes[j];
    125 			dist *= dist;
    126 			
    127 			// compare with the best so far
    128 			if( dist < least )
    129 			{
    130 				least = dist;
    131 				index = j;
    132 			}
    133 		}
    134 		
    135 		// save this index and accumulate the error
    136 		indices[i] = ( u8 )index;
    137 		err += least;
    138 	}
    139 	
    140 	// return the total error
    141 	return err;
    142 }
    143 
    144 static void WriteAlphaBlock( int alpha0, int alpha1, u8 const* indices, void* block )
    145 {
    146 	u8* bytes = reinterpret_cast< u8* >( block );
    147 	
    148 	// write the first two bytes
    149 	bytes[0] = ( u8 )alpha0;
    150 	bytes[1] = ( u8 )alpha1;
    151 	
    152 	// pack the indices with 3 bits each
    153 	u8* dest = bytes + 2;
    154 	u8 const* src = indices;
    155 	for( int i = 0; i < 2; ++i )
    156 	{
    157 		// pack 8 3-bit values
    158 		int value = 0;
    159 		for( int j = 0; j < 8; ++j )
    160 		{
    161 			int index = *src++;
    162 			value |= ( index << 3*j );
    163 		}
    164 			
    165 		// store in 3 bytes
    166 		for( int j = 0; j < 3; ++j )
    167 		{
    168 			int byte = ( value >> 8*j ) & 0xff;
    169 			*dest++ = ( u8 )byte;
    170 		}
    171 	}
    172 }
    173 
    174 static void WriteAlphaBlock5( int alpha0, int alpha1, u8 const* indices, void* block )
    175 {
    176 	// check the relative values of the endpoints
    177 	if( alpha0 > alpha1 )
    178 	{
    179 		// swap the indices
    180 		u8 swapped[16];
    181 		for( int i = 0; i < 16; ++i )
    182 		{
    183 			u8 index = indices[i];
    184 			if( index == 0 )
    185 				swapped[i] = 1;
    186 			else if( index == 1 )
    187 				swapped[i] = 0;
    188 			else if( index <= 5 )
    189 				swapped[i] = 7 - index;
    190 			else 
    191 				swapped[i] = index;
    192 		}
    193 		
    194 		// write the block
    195 		WriteAlphaBlock( alpha1, alpha0, swapped, block );
    196 	}
    197 	else
    198 	{
    199 		// write the block
    200 		WriteAlphaBlock( alpha0, alpha1, indices, block );
    201 	}	
    202 }
    203 
    204 static void WriteAlphaBlock7( int alpha0, int alpha1, u8 const* indices, void* block )
    205 {
    206 	// check the relative values of the endpoints
    207 	if( alpha0 < alpha1 )
    208 	{
    209 		// swap the indices
    210 		u8 swapped[16];
    211 		for( int i = 0; i < 16; ++i )
    212 		{
    213 			u8 index = indices[i];
    214 			if( index == 0 )
    215 				swapped[i] = 1;
    216 			else if( index == 1 )
    217 				swapped[i] = 0;
    218 			else
    219 				swapped[i] = 9 - index;
    220 		}
    221 		
    222 		// write the block
    223 		WriteAlphaBlock( alpha1, alpha0, swapped, block );
    224 	}
    225 	else
    226 	{
    227 		// write the block
    228 		WriteAlphaBlock( alpha0, alpha1, indices, block );
    229 	}	
    230 }
    231 
    232 void CompressAlphaDxt5( u8 const* rgba, int mask, void* block )
    233 {
    234 	// get the range for 5-alpha and 7-alpha interpolation
    235 	int min5 = 255;
    236 	int max5 = 0;
    237 	int min7 = 255;
    238 	int max7 = 0;
    239 	for( int i = 0; i < 16; ++i )
    240 	{
    241 		// check this pixel is valid
    242 		int bit = 1 << i;
    243 		if( ( mask & bit ) == 0 )
    244 			continue;
    245 
    246 		// incorporate into the min/max
    247 		int value = rgba[4*i + 3];
    248 		if( value < min7 )
    249 			min7 = value;
    250 		if( value > max7 )
    251 			max7 = value;
    252 		if( value != 0 && value < min5 )
    253 			min5 = value;
    254 		if( value != 255 && value > max5 )
    255 			max5 = value;
    256 	}
    257 	
    258 	// handle the case that no valid range was found
    259 	if( min5 > max5 )
    260 		min5 = max5;
    261 	if( min7 > max7 )
    262 		min7 = max7;
    263 		
    264 	// fix the range to be the minimum in each case
    265 	FixRange( min5, max5, 5 );
    266 	FixRange( min7, max7, 7 );
    267 	
    268 	// set up the 5-alpha code book
    269 	u8 codes5[8];
    270 	codes5[0] = ( u8 )min5;
    271 	codes5[1] = ( u8 )max5;
    272 	for( int i = 1; i < 5; ++i )
    273 		codes5[1 + i] = ( u8 )( ( ( 5 - i )*min5 + i*max5 )/5 );
    274 	codes5[6] = 0;
    275 	codes5[7] = 255;
    276 	
    277 	// set up the 7-alpha code book
    278 	u8 codes7[8];
    279 	codes7[0] = ( u8 )min7;
    280 	codes7[1] = ( u8 )max7;
    281 	for( int i = 1; i < 7; ++i )
    282 		codes7[1 + i] = ( u8 )( ( ( 7 - i )*min7 + i*max7 )/7 );
    283 		
    284 	// fit the data to both code books
    285 	u8 indices5[16];
    286 	u8 indices7[16];
    287 	int err5 = FitCodes( rgba, mask, codes5, indices5 );
    288 	int err7 = FitCodes( rgba, mask, codes7, indices7 );
    289 	
    290 	// save the block with least error
    291 	if( err5 <= err7 )
    292 		WriteAlphaBlock5( min5, max5, indices5, block );
    293 	else
    294 		WriteAlphaBlock7( min7, max7, indices7, block );
    295 }
    296 
    297 void DecompressAlphaDxt5( u8* rgba, void const* block )
    298 {
    299 	// get the two alpha values
    300 	u8 const* bytes = reinterpret_cast< u8 const* >( block );
    301 	int alpha0 = bytes[0];
    302 	int alpha1 = bytes[1];
    303 	
    304 	// compare the values to build the codebook
    305 	u8 codes[8];
    306 	codes[0] = ( u8 )alpha0;
    307 	codes[1] = ( u8 )alpha1;
    308 	if( alpha0 <= alpha1 )
    309 	{
    310 		// use 5-alpha codebook
    311 		for( int i = 1; i < 5; ++i )
    312 			codes[1 + i] = ( u8 )( ( ( 5 - i )*alpha0 + i*alpha1 )/5 );
    313 		codes[6] = 0;
    314 		codes[7] = 255;
    315 	}
    316 	else
    317 	{
    318 		// use 7-alpha codebook
    319 		for( int i = 1; i < 7; ++i )
    320 			codes[1 + i] = ( u8 )( ( ( 7 - i )*alpha0 + i*alpha1 )/7 );
    321 	}
    322 	
    323 	// decode the indices
    324 	u8 indices[16];
    325 	u8 const* src = bytes + 2;
    326 	u8* dest = indices;
    327 	for( int i = 0; i < 2; ++i )
    328 	{
    329 		// grab 3 bytes
    330 		int value = 0;
    331 		for( int j = 0; j < 3; ++j )
    332 		{
    333 			int byte = *src++;
    334 			value |= ( byte << 8*j );
    335 		}
    336 		
    337 		// unpack 8 3-bit values from it
    338 		for( int j = 0; j < 8; ++j )
    339 		{
    340 			int index = ( value >> 3*j ) & 0x7;
    341 			*dest++ = ( u8 )index;
    342 		}
    343 	}
    344 	
    345 	// write out the indexed codebook values
    346 	for( int i = 0; i < 16; ++i )
    347 		rgba[4*i + 3] = codes[indices[i]];
    348 }
    349 
    350 } // namespace squish