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 #include <algorithm>
28 
29 namespace squish {
30 
FloatToInt(float a,int limit)31 static int FloatToInt( float a, int limit )
32 {
33 	// use ANSI round-to-zero behaviour to get round-to-nearest
34 	int i = ( int )( a + 0.5f );
35 
36 	// clamp to the limit
37 	if( i < 0 )
38 		i = 0;
39 	else if( i > limit )
40 		i = limit;
41 
42 	// done
43 	return i;
44 }
45 
CompressAlphaDxt3(u8 const * rgba,int mask,void * block)46 void CompressAlphaDxt3( u8 const* rgba, int mask, void* block )
47 {
48 	u8* bytes = reinterpret_cast< u8* >( block );
49 
50 	// quantise and pack the alpha values pairwise
51 	for( int i = 0; i < 8; ++i )
52 	{
53 		// quantise down to 4 bits
54 		float alpha1 = ( float )rgba[8*i + 3] * ( 15.0f/255.0f );
55 		float alpha2 = ( float )rgba[8*i + 7] * ( 15.0f/255.0f );
56 		int quant1 = FloatToInt( alpha1, 15 );
57 		int quant2 = FloatToInt( alpha2, 15 );
58 
59 		// set alpha to zero where masked
60 		int bit1 = 1 << ( 2*i );
61 		int bit2 = 1 << ( 2*i + 1 );
62 		if( ( mask & bit1 ) == 0 )
63 			quant1 = 0;
64 		if( ( mask & bit2 ) == 0 )
65 			quant2 = 0;
66 
67 		// pack into the byte
68 		bytes[i] = ( u8 )( quant1 | ( quant2 << 4 ) );
69 	}
70 }
71 
DecompressAlphaDxt3(u8 * rgba,void const * block)72 void DecompressAlphaDxt3( u8* rgba, void const* block )
73 {
74 	u8 const* bytes = reinterpret_cast< u8 const* >( block );
75 
76 	// unpack the alpha values pairwise
77 	for( int i = 0; i < 8; ++i )
78 	{
79 		// quantise down to 4 bits
80 		u8 quant = bytes[i];
81 
82 		// unpack the values
83 		u8 lo = quant & 0x0f;
84 		u8 hi = quant & 0xf0;
85 
86 		// convert back up to bytes
87 		rgba[8*i + 3] = lo | ( lo << 4 );
88 		rgba[8*i + 7] = hi | ( hi >> 4 );
89 	}
90 }
91 
FixRange(int & min,int & max,int steps)92 static void FixRange( int& min, int& max, int steps )
93 {
94 	if( max - min < steps )
95 		max = std::min( min + steps, 255 );
96 	if( max - min < steps )
97 		min = std::max( 0, max - steps );
98 }
99 
FitCodes(u8 const * rgba,int mask,u8 const * codes,u8 * indices)100 static int FitCodes( u8 const* rgba, int mask, u8 const* codes, u8* indices )
101 {
102 	// fit each alpha value to the codebook
103 	int err = 0;
104 	for( int i = 0; i < 16; ++i )
105 	{
106 		// check this pixel is valid
107 		int bit = 1 << i;
108 		if( ( mask & bit ) == 0 )
109 		{
110 			// use the first code
111 			indices[i] = 0;
112 			continue;
113 		}
114 
115 		// find the least error and corresponding index
116 		int value = rgba[4*i + 3];
117 		int least = INT_MAX;
118 		int index = 0;
119 		for( int j = 0; j < 8; ++j )
120 		{
121 			// get the squared error from this code
122 			int dist = ( int )value - ( int )codes[j];
123 			dist *= dist;
124 
125 			// compare with the best so far
126 			if( dist < least )
127 			{
128 				least = dist;
129 				index = j;
130 			}
131 		}
132 
133 		// save this index and accumulate the error
134 		indices[i] = ( u8 )index;
135 		err += least;
136 	}
137 
138 	// return the total error
139 	return err;
140 }
141 
WriteAlphaBlock(int alpha0,int alpha1,u8 const * indices,void * block)142 static void WriteAlphaBlock( int alpha0, int alpha1, u8 const* indices, void* block )
143 {
144 	u8* bytes = reinterpret_cast< u8* >( block );
145 
146 	// write the first two bytes
147 	bytes[0] = ( u8 )alpha0;
148 	bytes[1] = ( u8 )alpha1;
149 
150 	// pack the indices with 3 bits each
151 	u8* dest = bytes + 2;
152 	u8 const* src = indices;
153 	for( int i = 0; i < 2; ++i )
154 	{
155 		// pack 8 3-bit values
156 		int value = 0;
157 		for( int j = 0; j < 8; ++j )
158 		{
159 			int index = *src++;
160 			value |= ( index << 3*j );
161 		}
162 
163 		// store in 3 bytes
164 		for( int j = 0; j < 3; ++j )
165 		{
166 			int byte = ( value >> 8*j ) & 0xff;
167 			*dest++ = ( u8 )byte;
168 		}
169 	}
170 }
171 
WriteAlphaBlock5(int alpha0,int alpha1,u8 const * indices,void * block)172 static void WriteAlphaBlock5( int alpha0, int alpha1, u8 const* indices, void* block )
173 {
174 	// check the relative values of the endpoints
175 	if( alpha0 > alpha1 )
176 	{
177 		// swap the indices
178 		u8 swapped[16];
179 		for( int i = 0; i < 16; ++i )
180 		{
181 			u8 index = indices[i];
182 			if( index == 0 )
183 				swapped[i] = 1;
184 			else if( index == 1 )
185 				swapped[i] = 0;
186 			else if( index <= 5 )
187 				swapped[i] = 7 - index;
188 			else
189 				swapped[i] = index;
190 		}
191 
192 		// write the block
193 		WriteAlphaBlock( alpha1, alpha0, swapped, block );
194 	}
195 	else
196 	{
197 		// write the block
198 		WriteAlphaBlock( alpha0, alpha1, indices, block );
199 	}
200 }
201 
WriteAlphaBlock7(int alpha0,int alpha1,u8 const * indices,void * block)202 static void WriteAlphaBlock7( int alpha0, int alpha1, u8 const* indices, void* block )
203 {
204 	// check the relative values of the endpoints
205 	if( alpha0 < alpha1 )
206 	{
207 		// swap the indices
208 		u8 swapped[16];
209 		for( int i = 0; i < 16; ++i )
210 		{
211 			u8 index = indices[i];
212 			if( index == 0 )
213 				swapped[i] = 1;
214 			else if( index == 1 )
215 				swapped[i] = 0;
216 			else
217 				swapped[i] = 9 - index;
218 		}
219 
220 		// write the block
221 		WriteAlphaBlock( alpha1, alpha0, swapped, block );
222 	}
223 	else
224 	{
225 		// write the block
226 		WriteAlphaBlock( alpha0, alpha1, indices, block );
227 	}
228 }
229 
CompressAlphaDxt5(u8 const * rgba,int mask,void * block)230 void CompressAlphaDxt5( u8 const* rgba, int mask, void* block )
231 {
232 	// get the range for 5-alpha and 7-alpha interpolation
233 	int min5 = 255;
234 	int max5 = 0;
235 	int min7 = 255;
236 	int max7 = 0;
237 	for( int i = 0; i < 16; ++i )
238 	{
239 		// check this pixel is valid
240 		int bit = 1 << i;
241 		if( ( mask & bit ) == 0 )
242 			continue;
243 
244 		// incorporate into the min/max
245 		int value = rgba[4*i + 3];
246 		if( value < min7 )
247 			min7 = value;
248 		if( value > max7 )
249 			max7 = value;
250 		if( value != 0 && value < min5 )
251 			min5 = value;
252 		if( value != 255 && value > max5 )
253 			max5 = value;
254 	}
255 
256 	// handle the case that no valid range was found
257 	if( min5 > max5 )
258 		min5 = max5;
259 	if( min7 > max7 )
260 		min7 = max7;
261 
262 	// fix the range to be the minimum in each case
263 	FixRange( min5, max5, 5 );
264 	FixRange( min7, max7, 7 );
265 
266 	// set up the 5-alpha code book
267 	u8 codes5[8];
268 	codes5[0] = ( u8 )min5;
269 	codes5[1] = ( u8 )max5;
270 	for( int i = 1; i < 5; ++i )
271 		codes5[1 + i] = ( u8 )( ( ( 5 - i )*min5 + i*max5 )/5 );
272 	codes5[6] = 0;
273 	codes5[7] = 255;
274 
275 	// set up the 7-alpha code book
276 	u8 codes7[8];
277 	codes7[0] = ( u8 )min7;
278 	codes7[1] = ( u8 )max7;
279 	for( int i = 1; i < 7; ++i )
280 		codes7[1 + i] = ( u8 )( ( ( 7 - i )*min7 + i*max7 )/7 );
281 
282 	// fit the data to both code books
283 	u8 indices5[16];
284 	u8 indices7[16];
285 	int err5 = FitCodes( rgba, mask, codes5, indices5 );
286 	int err7 = FitCodes( rgba, mask, codes7, indices7 );
287 
288 	// save the block with least error
289 	if( err5 <= err7 )
290 		WriteAlphaBlock5( min5, max5, indices5, block );
291 	else
292 		WriteAlphaBlock7( min7, max7, indices7, block );
293 }
294 
DecompressAlphaDxt5(u8 * rgba,void const * block)295 void DecompressAlphaDxt5( u8* rgba, void const* block )
296 {
297 	// get the two alpha values
298 	u8 const* bytes = reinterpret_cast< u8 const* >( block );
299 	int alpha0 = bytes[0];
300 	int alpha1 = bytes[1];
301 
302 	// compare the values to build the codebook
303 	u8 codes[8];
304 	codes[0] = ( u8 )alpha0;
305 	codes[1] = ( u8 )alpha1;
306 	if( alpha0 <= alpha1 )
307 	{
308 		// use 5-alpha codebook
309 		for( int i = 1; i < 5; ++i )
310 			codes[1 + i] = ( u8 )( ( ( 5 - i )*alpha0 + i*alpha1 )/5 );
311 		codes[6] = 0;
312 		codes[7] = 255;
313 	}
314 	else
315 	{
316 		// use 7-alpha codebook
317 		for( int i = 1; i < 7; ++i )
318 			codes[1 + i] = ( u8 )( ( ( 7 - i )*alpha0 + i*alpha1 )/7 );
319 	}
320 
321 	// decode the indices
322 	u8 indices[16];
323 	u8 const* src = bytes + 2;
324 	u8* dest = indices;
325 	for( int i = 0; i < 2; ++i )
326 	{
327 		// grab 3 bytes
328 		int value = 0;
329 		for( int j = 0; j < 3; ++j )
330 		{
331 			int byte = *src++;
332 			value |= ( byte << 8*j );
333 		}
334 
335 		// unpack 8 3-bit values from it
336 		for( int j = 0; j < 8; ++j )
337 		{
338 			int index = ( value >> 3*j ) & 0x7;
339 			*dest++ = ( u8 )index;
340 		}
341 	}
342 
343 	// write out the indexed codebook values
344 	for( int i = 0; i < 16; ++i )
345 		rgba[4*i + 3] = codes[indices[i]];
346 }
347 
348 } // namespace squish
349