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 
FloatToInt(float a,int limit)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 
CompressAlphaDxt3(u8 const * rgba,int mask,void * block)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 
DecompressAlphaDxt3(u8 * rgba,void const * block)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 
FixRange(int & min,int & max,int steps)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 
FitCodes(u8 const * rgba,int mask,u8 const * codes,u8 * indices)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 
WriteAlphaBlock(int alpha0,int alpha1,u8 const * indices,void * block)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 
WriteAlphaBlock5(int alpha0,int alpha1,u8 const * indices,void * block)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 
WriteAlphaBlock7(int alpha0,int alpha1,u8 const * indices,void * block)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 
CompressAlphaDxt5(u8 const * rgba,int mask,void * block)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 
DecompressAlphaDxt5(u8 * rgba,void const * block)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
351