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 |
351 | |