1 | /* |
2 | Jonathan Dummer |
3 | 2007-07-31-10.32 |
4 | |
5 | simple DXT compression / decompression code |
6 | |
7 | public domain |
8 | */ |
9 | |
10 | #include "image_DXT.h" |
11 | #include <math.h> |
12 | #include <stdlib.h> |
13 | #include <string.h> |
14 | #include <stdio.h> |
15 | |
16 | /* set this =1 if you want to use the covarince matrix method... |
17 | which is better than my method of using standard deviations |
18 | overall, except on the infintesimal chance that the power |
19 | method fails for finding the largest eigenvector */ |
20 | #define USE_COV_MAT 1 |
21 | |
22 | /********* Function Prototypes *********/ |
23 | /* |
24 | Takes a 4x4 block of pixels and compresses it into 8 bytes |
25 | in DXT1 format (color only, no alpha). Speed is valued |
26 | over prettyness, at least for now. |
27 | */ |
28 | void compress_DDS_color_block( |
29 | int channels, |
30 | const unsigned char *const uncompressed, |
31 | unsigned char compressed[8] ); |
32 | /* |
33 | Takes a 4x4 block of pixels and compresses the alpha |
34 | component it into 8 bytes for use in DXT5 DDS files. |
35 | Speed is valued over prettyness, at least for now. |
36 | */ |
37 | void compress_DDS_alpha_block( |
38 | const unsigned char *const uncompressed, |
39 | unsigned char compressed[8] ); |
40 | |
41 | /********* Actual Exposed Functions *********/ |
42 | int |
43 | save_image_as_DDS |
44 | ( |
45 | const char *filename, |
46 | int width, int height, int channels, |
47 | const unsigned char *const data |
48 | ) |
49 | { |
50 | /* variables */ |
51 | FILE *fout; |
52 | unsigned char *DDS_data; |
53 | DDS_header ; |
54 | int DDS_size; |
55 | /* error check */ |
56 | if( (NULL == filename) || |
57 | (width < 1) || (height < 1) || |
58 | (channels < 1) || (channels > 4) || |
59 | (data == NULL ) ) |
60 | { |
61 | return 0; |
62 | } |
63 | /* Convert the image */ |
64 | if( (channels & 1) == 1 ) |
65 | { |
66 | /* no alpha, just use DXT1 */ |
67 | DDS_data = convert_image_to_DXT1( data, width, height, channels, &DDS_size ); |
68 | } else |
69 | { |
70 | /* has alpha, so use DXT5 */ |
71 | DDS_data = convert_image_to_DXT5( data, width, height, channels, &DDS_size ); |
72 | } |
73 | /* save it */ |
74 | memset( &header, 0, sizeof( DDS_header ) ); |
75 | header.dwMagic = ('D' << 0) | ('D' << 8) | ('S' << 16) | (' ' << 24); |
76 | header.dwSize = 124; |
77 | header.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_LINEARSIZE; |
78 | header.dwWidth = width; |
79 | header.dwHeight = height; |
80 | header.dwPitchOrLinearSize = DDS_size; |
81 | header.sPixelFormat.dwSize = 32; |
82 | header.sPixelFormat.dwFlags = DDPF_FOURCC; |
83 | if( (channels & 1) == 1 ) |
84 | { |
85 | header.sPixelFormat.dwFourCC = ('D' << 0) | ('X' << 8) | ('T' << 16) | ('1' << 24); |
86 | } else |
87 | { |
88 | header.sPixelFormat.dwFourCC = ('D' << 0) | ('X' << 8) | ('T' << 16) | ('5' << 24); |
89 | } |
90 | header.sCaps.dwCaps1 = DDSCAPS_TEXTURE; |
91 | /* write it out */ |
92 | fout = fopen( filename, "wb" ); |
93 | fwrite( &header, sizeof( DDS_header ), 1, fout ); |
94 | fwrite( DDS_data, 1, DDS_size, fout ); |
95 | fclose( fout ); |
96 | /* done */ |
97 | free( DDS_data ); |
98 | return 1; |
99 | } |
100 | |
101 | unsigned char* convert_image_to_DXT1( |
102 | const unsigned char *const uncompressed, |
103 | int width, int height, int channels, |
104 | int *out_size ) |
105 | { |
106 | unsigned char *compressed; |
107 | int i, j, x, y; |
108 | unsigned char ublock[16*3]; |
109 | unsigned char cblock[8]; |
110 | int index = 0, chan_step = 1; |
111 | int block_count = 0; |
112 | /* error check */ |
113 | *out_size = 0; |
114 | if( (width < 1) || (height < 1) || |
115 | (NULL == uncompressed) || |
116 | (channels < 1) || (channels > 4) ) |
117 | { |
118 | return NULL; |
119 | } |
120 | /* for channels == 1 or 2, I do not step forward for R,G,B values */ |
121 | if( channels < 3 ) |
122 | { |
123 | chan_step = 0; |
124 | } |
125 | /* get the RAM for the compressed image |
126 | (8 bytes per 4x4 pixel block) */ |
127 | *out_size = ((width+3) >> 2) * ((height+3) >> 2) * 8; |
128 | compressed = (unsigned char*)malloc( *out_size ); |
129 | /* go through each block */ |
130 | for( j = 0; j < height; j += 4 ) |
131 | { |
132 | for( i = 0; i < width; i += 4 ) |
133 | { |
134 | /* copy this block into a new one */ |
135 | int idx = 0; |
136 | int mx = 4, my = 4; |
137 | if( j+4 >= height ) |
138 | { |
139 | my = height - j; |
140 | } |
141 | if( i+4 >= width ) |
142 | { |
143 | mx = width - i; |
144 | } |
145 | for( y = 0; y < my; ++y ) |
146 | { |
147 | for( x = 0; x < mx; ++x ) |
148 | { |
149 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels]; |
150 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step]; |
151 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step+chan_step]; |
152 | } |
153 | for( x = mx; x < 4; ++x ) |
154 | { |
155 | ublock[idx++] = ublock[0]; |
156 | ublock[idx++] = ublock[1]; |
157 | ublock[idx++] = ublock[2]; |
158 | } |
159 | } |
160 | for( y = my; y < 4; ++y ) |
161 | { |
162 | for( x = 0; x < 4; ++x ) |
163 | { |
164 | ublock[idx++] = ublock[0]; |
165 | ublock[idx++] = ublock[1]; |
166 | ublock[idx++] = ublock[2]; |
167 | } |
168 | } |
169 | /* compress the block */ |
170 | ++block_count; |
171 | compress_DDS_color_block( 3, ublock, cblock ); |
172 | /* copy the data from the block into the main block */ |
173 | for( x = 0; x < 8; ++x ) |
174 | { |
175 | compressed[index++] = cblock[x]; |
176 | } |
177 | } |
178 | } |
179 | return compressed; |
180 | } |
181 | |
182 | unsigned char* convert_image_to_DXT5( |
183 | const unsigned char *const uncompressed, |
184 | int width, int height, int channels, |
185 | int *out_size ) |
186 | { |
187 | unsigned char *compressed; |
188 | int i, j, x, y; |
189 | unsigned char ublock[16*4]; |
190 | unsigned char cblock[8]; |
191 | int index = 0, chan_step = 1; |
192 | int block_count = 0, has_alpha; |
193 | /* error check */ |
194 | *out_size = 0; |
195 | if( (width < 1) || (height < 1) || |
196 | (NULL == uncompressed) || |
197 | (channels < 1) || ( channels > 4) ) |
198 | { |
199 | return NULL; |
200 | } |
201 | /* for channels == 1 or 2, I do not step forward for R,G,B vales */ |
202 | if( channels < 3 ) |
203 | { |
204 | chan_step = 0; |
205 | } |
206 | /* # channels = 1 or 3 have no alpha, 2 & 4 do have alpha */ |
207 | has_alpha = 1 - (channels & 1); |
208 | /* get the RAM for the compressed image |
209 | (16 bytes per 4x4 pixel block) */ |
210 | *out_size = ((width+3) >> 2) * ((height+3) >> 2) * 16; |
211 | compressed = (unsigned char*)malloc( *out_size ); |
212 | /* go through each block */ |
213 | for( j = 0; j < height; j += 4 ) |
214 | { |
215 | for( i = 0; i < width; i += 4 ) |
216 | { |
217 | /* local variables, and my block counter */ |
218 | int idx = 0; |
219 | int mx = 4, my = 4; |
220 | if( j+4 >= height ) |
221 | { |
222 | my = height - j; |
223 | } |
224 | if( i+4 >= width ) |
225 | { |
226 | mx = width - i; |
227 | } |
228 | for( y = 0; y < my; ++y ) |
229 | { |
230 | for( x = 0; x < mx; ++x ) |
231 | { |
232 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels]; |
233 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step]; |
234 | ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step+chan_step]; |
235 | ublock[idx++] = |
236 | has_alpha * uncompressed[(j+y)*width*channels+(i+x)*channels+channels-1] |
237 | + (1-has_alpha)*255; |
238 | } |
239 | for( x = mx; x < 4; ++x ) |
240 | { |
241 | ublock[idx++] = ublock[0]; |
242 | ublock[idx++] = ublock[1]; |
243 | ublock[idx++] = ublock[2]; |
244 | ublock[idx++] = ublock[3]; |
245 | } |
246 | } |
247 | for( y = my; y < 4; ++y ) |
248 | { |
249 | for( x = 0; x < 4; ++x ) |
250 | { |
251 | ublock[idx++] = ublock[0]; |
252 | ublock[idx++] = ublock[1]; |
253 | ublock[idx++] = ublock[2]; |
254 | ublock[idx++] = ublock[3]; |
255 | } |
256 | } |
257 | /* now compress the alpha block */ |
258 | compress_DDS_alpha_block( ublock, cblock ); |
259 | /* copy the data from the compressed alpha block into the main buffer */ |
260 | for( x = 0; x < 8; ++x ) |
261 | { |
262 | compressed[index++] = cblock[x]; |
263 | } |
264 | /* then compress the color block */ |
265 | ++block_count; |
266 | compress_DDS_color_block( 4, ublock, cblock ); |
267 | /* copy the data from the compressed color block into the main buffer */ |
268 | for( x = 0; x < 8; ++x ) |
269 | { |
270 | compressed[index++] = cblock[x]; |
271 | } |
272 | } |
273 | } |
274 | return compressed; |
275 | } |
276 | |
277 | /********* Helper Functions *********/ |
278 | int convert_bit_range( int c, int from_bits, int to_bits ) |
279 | { |
280 | int b = (1 << (from_bits - 1)) + c * ((1 << to_bits) - 1); |
281 | return (b + (b >> from_bits)) >> from_bits; |
282 | } |
283 | |
284 | int rgb_to_565( int r, int g, int b ) |
285 | { |
286 | return |
287 | (convert_bit_range( r, 8, 5 ) << 11) | |
288 | (convert_bit_range( g, 8, 6 ) << 05) | |
289 | (convert_bit_range( b, 8, 5 ) << 00); |
290 | } |
291 | |
292 | void rgb_888_from_565( unsigned int c, int *r, int *g, int *b ) |
293 | { |
294 | *r = convert_bit_range( (c >> 11) & 31, 5, 8 ); |
295 | *g = convert_bit_range( (c >> 05) & 63, 6, 8 ); |
296 | *b = convert_bit_range( (c >> 00) & 31, 5, 8 ); |
297 | } |
298 | |
299 | void compute_color_line_STDEV( |
300 | const unsigned char *const uncompressed, |
301 | int channels, |
302 | float point[3], float direction[3] ) |
303 | { |
304 | const float inv_16 = 1.0f / 16.0f; |
305 | int i; |
306 | float sum_r = 0.0f, sum_g = 0.0f, sum_b = 0.0f; |
307 | float sum_rr = 0.0f, sum_gg = 0.0f, sum_bb = 0.0f; |
308 | float sum_rg = 0.0f, sum_rb = 0.0f, sum_gb = 0.0f; |
309 | /* calculate all data needed for the covariance matrix |
310 | ( to compare with _rygdxt code) */ |
311 | for( i = 0; i < 16*channels; i += channels ) |
312 | { |
313 | sum_r += uncompressed[i+0]; |
314 | sum_rr += uncompressed[i+0] * uncompressed[i+0]; |
315 | sum_g += uncompressed[i+1]; |
316 | sum_gg += uncompressed[i+1] * uncompressed[i+1]; |
317 | sum_b += uncompressed[i+2]; |
318 | sum_bb += uncompressed[i+2] * uncompressed[i+2]; |
319 | sum_rg += uncompressed[i+0] * uncompressed[i+1]; |
320 | sum_rb += uncompressed[i+0] * uncompressed[i+2]; |
321 | sum_gb += uncompressed[i+1] * uncompressed[i+2]; |
322 | } |
323 | /* convert the sums to averages */ |
324 | sum_r *= inv_16; |
325 | sum_g *= inv_16; |
326 | sum_b *= inv_16; |
327 | /* and convert the squares to the squares of the value - avg_value */ |
328 | sum_rr -= 16.0f * sum_r * sum_r; |
329 | sum_gg -= 16.0f * sum_g * sum_g; |
330 | sum_bb -= 16.0f * sum_b * sum_b; |
331 | sum_rg -= 16.0f * sum_r * sum_g; |
332 | sum_rb -= 16.0f * sum_r * sum_b; |
333 | sum_gb -= 16.0f * sum_g * sum_b; |
334 | /* the point on the color line is the average */ |
335 | point[0] = sum_r; |
336 | point[1] = sum_g; |
337 | point[2] = sum_b; |
338 | #if USE_COV_MAT |
339 | /* |
340 | The following idea was from ryg. |
341 | (https://mollyrocket.com/forums/viewtopic.php?t=392) |
342 | The method worked great (less RMSE than mine) most of |
343 | the time, but had some issues handling some simple |
344 | boundary cases, like full green next to full red, |
345 | which would generate a covariance matrix like this: |
346 | |
347 | | 1 -1 0 | |
348 | | -1 1 0 | |
349 | | 0 0 0 | |
350 | |
351 | For a given starting vector, the power method can |
352 | generate all zeros! So no starting with {1,1,1} |
353 | as I was doing! This kind of error is still a |
354 | slight posibillity, but will be very rare. |
355 | */ |
356 | /* use the covariance matrix directly |
357 | (1st iteration, don't use all 1.0 values!) */ |
358 | sum_r = 1.0f; |
359 | sum_g = 2.718281828f; |
360 | sum_b = 3.141592654f; |
361 | direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb; |
362 | direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb; |
363 | direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb; |
364 | /* 2nd iteration, use results from the 1st guy */ |
365 | sum_r = direction[0]; |
366 | sum_g = direction[1]; |
367 | sum_b = direction[2]; |
368 | direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb; |
369 | direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb; |
370 | direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb; |
371 | /* 3rd iteration, use results from the 2nd guy */ |
372 | sum_r = direction[0]; |
373 | sum_g = direction[1]; |
374 | sum_b = direction[2]; |
375 | direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb; |
376 | direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb; |
377 | direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb; |
378 | #else |
379 | /* use my standard deviation method |
380 | (very robust, a tiny bit slower and less accurate) */ |
381 | direction[0] = sqrt( sum_rr ); |
382 | direction[1] = sqrt( sum_gg ); |
383 | direction[2] = sqrt( sum_bb ); |
384 | /* which has a greater component */ |
385 | if( sum_gg > sum_rr ) |
386 | { |
387 | /* green has greater component, so base the other signs off of green */ |
388 | if( sum_rg < 0.0f ) |
389 | { |
390 | direction[0] = -direction[0]; |
391 | } |
392 | if( sum_gb < 0.0f ) |
393 | { |
394 | direction[2] = -direction[2]; |
395 | } |
396 | } else |
397 | { |
398 | /* red has a greater component */ |
399 | if( sum_rg < 0.0f ) |
400 | { |
401 | direction[1] = -direction[1]; |
402 | } |
403 | if( sum_rb < 0.0f ) |
404 | { |
405 | direction[2] = -direction[2]; |
406 | } |
407 | } |
408 | #endif |
409 | } |
410 | |
411 | void LSE_master_colors_max_min( |
412 | int *cmax, int *cmin, |
413 | int channels, |
414 | const unsigned char *const uncompressed ) |
415 | { |
416 | int i, j; |
417 | /* the master colors */ |
418 | int c0[3], c1[3]; |
419 | /* used for fitting the line */ |
420 | float sum_x[] = { 0.0f, 0.0f, 0.0f }; |
421 | float sum_x2[] = { 0.0f, 0.0f, 0.0f }; |
422 | float dot_max = 1.0f, dot_min = -1.0f; |
423 | float vec_len2 = 0.0f; |
424 | float dot; |
425 | /* error check */ |
426 | if( (channels < 3) || (channels > 4) ) |
427 | { |
428 | return; |
429 | } |
430 | compute_color_line_STDEV( uncompressed, channels, sum_x, sum_x2 ); |
431 | vec_len2 = 1.0f / ( 0.00001f + |
432 | sum_x2[0]*sum_x2[0] + sum_x2[1]*sum_x2[1] + sum_x2[2]*sum_x2[2] ); |
433 | /* finding the max and min vector values */ |
434 | dot_max = |
435 | ( |
436 | sum_x2[0] * uncompressed[0] + |
437 | sum_x2[1] * uncompressed[1] + |
438 | sum_x2[2] * uncompressed[2] |
439 | ); |
440 | dot_min = dot_max; |
441 | for( i = 1; i < 16; ++i ) |
442 | { |
443 | dot = |
444 | ( |
445 | sum_x2[0] * uncompressed[i*channels+0] + |
446 | sum_x2[1] * uncompressed[i*channels+1] + |
447 | sum_x2[2] * uncompressed[i*channels+2] |
448 | ); |
449 | if( dot < dot_min ) |
450 | { |
451 | dot_min = dot; |
452 | } else if( dot > dot_max ) |
453 | { |
454 | dot_max = dot; |
455 | } |
456 | } |
457 | /* and the offset (from the average location) */ |
458 | dot = sum_x2[0]*sum_x[0] + sum_x2[1]*sum_x[1] + sum_x2[2]*sum_x[2]; |
459 | dot_min -= dot; |
460 | dot_max -= dot; |
461 | /* post multiply by the scaling factor */ |
462 | dot_min *= vec_len2; |
463 | dot_max *= vec_len2; |
464 | /* OK, build the master colors */ |
465 | for( i = 0; i < 3; ++i ) |
466 | { |
467 | /* color 0 */ |
468 | c0[i] = (int)(0.5f + sum_x[i] + dot_max * sum_x2[i]); |
469 | if( c0[i] < 0 ) |
470 | { |
471 | c0[i] = 0; |
472 | } else if( c0[i] > 255 ) |
473 | { |
474 | c0[i] = 255; |
475 | } |
476 | /* color 1 */ |
477 | c1[i] = (int)(0.5f + sum_x[i] + dot_min * sum_x2[i]); |
478 | if( c1[i] < 0 ) |
479 | { |
480 | c1[i] = 0; |
481 | } else if( c1[i] > 255 ) |
482 | { |
483 | c1[i] = 255; |
484 | } |
485 | } |
486 | /* down_sample (with rounding?) */ |
487 | i = rgb_to_565( c0[0], c0[1], c0[2] ); |
488 | j = rgb_to_565( c1[0], c1[1], c1[2] ); |
489 | if( i > j ) |
490 | { |
491 | *cmax = i; |
492 | *cmin = j; |
493 | } else |
494 | { |
495 | *cmax = j; |
496 | *cmin = i; |
497 | } |
498 | } |
499 | |
500 | void |
501 | compress_DDS_color_block |
502 | ( |
503 | int channels, |
504 | const unsigned char *const uncompressed, |
505 | unsigned char compressed[8] |
506 | ) |
507 | { |
508 | /* variables */ |
509 | int i; |
510 | int next_bit; |
511 | int enc_c0, enc_c1; |
512 | int c0[4], c1[4]; |
513 | float color_line[] = { 0.0f, 0.0f, 0.0f, 0.0f }; |
514 | float vec_len2 = 0.0f, dot_offset = 0.0f; |
515 | /* stupid order */ |
516 | int swizzle4[] = { 0, 2, 3, 1 }; |
517 | /* get the master colors */ |
518 | LSE_master_colors_max_min( &enc_c0, &enc_c1, channels, uncompressed ); |
519 | /* store the 565 color 0 and color 1 */ |
520 | compressed[0] = (enc_c0 >> 0) & 255; |
521 | compressed[1] = (enc_c0 >> 8) & 255; |
522 | compressed[2] = (enc_c1 >> 0) & 255; |
523 | compressed[3] = (enc_c1 >> 8) & 255; |
524 | /* zero out the compressed data */ |
525 | compressed[4] = 0; |
526 | compressed[5] = 0; |
527 | compressed[6] = 0; |
528 | compressed[7] = 0; |
529 | /* reconstitute the master color vectors */ |
530 | rgb_888_from_565( enc_c0, &c0[0], &c0[1], &c0[2] ); |
531 | rgb_888_from_565( enc_c1, &c1[0], &c1[1], &c1[2] ); |
532 | /* the new vector */ |
533 | vec_len2 = 0.0f; |
534 | for( i = 0; i < 3; ++i ) |
535 | { |
536 | color_line[i] = (float)(c1[i] - c0[i]); |
537 | vec_len2 += color_line[i] * color_line[i]; |
538 | } |
539 | if( vec_len2 > 0.0f ) |
540 | { |
541 | vec_len2 = 1.0f / vec_len2; |
542 | } |
543 | /* pre-proform the scaling */ |
544 | color_line[0] *= vec_len2; |
545 | color_line[1] *= vec_len2; |
546 | color_line[2] *= vec_len2; |
547 | /* compute the offset (constant) portion of the dot product */ |
548 | dot_offset = color_line[0]*c0[0] + color_line[1]*c0[1] + color_line[2]*c0[2]; |
549 | /* store the rest of the bits */ |
550 | next_bit = 8*4; |
551 | for( i = 0; i < 16; ++i ) |
552 | { |
553 | /* find the dot product of this color, to place it on the line |
554 | (should be [-1,1]) */ |
555 | int next_value = 0; |
556 | float dot_product = |
557 | color_line[0] * uncompressed[i*channels+0] + |
558 | color_line[1] * uncompressed[i*channels+1] + |
559 | color_line[2] * uncompressed[i*channels+2] - |
560 | dot_offset; |
561 | /* map to [0,3] */ |
562 | next_value = (int)( dot_product * 3.0f + 0.5f ); |
563 | if( next_value > 3 ) |
564 | { |
565 | next_value = 3; |
566 | } else if( next_value < 0 ) |
567 | { |
568 | next_value = 0; |
569 | } |
570 | /* OK, store this value */ |
571 | compressed[next_bit >> 3] |= swizzle4[ next_value ] << (next_bit & 7); |
572 | next_bit += 2; |
573 | } |
574 | /* done compressing to DXT1 */ |
575 | } |
576 | |
577 | void |
578 | compress_DDS_alpha_block |
579 | ( |
580 | const unsigned char *const uncompressed, |
581 | unsigned char compressed[8] |
582 | ) |
583 | { |
584 | /* variables */ |
585 | int i; |
586 | int next_bit; |
587 | int a0, a1; |
588 | float scale_me; |
589 | /* stupid order */ |
590 | int swizzle8[] = { 1, 7, 6, 5, 4, 3, 2, 0 }; |
591 | /* get the alpha limits (a0 > a1) */ |
592 | a0 = a1 = uncompressed[3]; |
593 | for( i = 4+3; i < 16*4; i += 4 ) |
594 | { |
595 | if( uncompressed[i] > a0 ) |
596 | { |
597 | a0 = uncompressed[i]; |
598 | } else if( uncompressed[i] < a1 ) |
599 | { |
600 | a1 = uncompressed[i]; |
601 | } |
602 | } |
603 | /* store those limits, and zero the rest of the compressed dataset */ |
604 | compressed[0] = a0; |
605 | compressed[1] = a1; |
606 | /* zero out the compressed data */ |
607 | compressed[2] = 0; |
608 | compressed[3] = 0; |
609 | compressed[4] = 0; |
610 | compressed[5] = 0; |
611 | compressed[6] = 0; |
612 | compressed[7] = 0; |
613 | /* store the all of the alpha values */ |
614 | next_bit = 8*2; |
615 | scale_me = 7.9999f / (a0 - a1); |
616 | for( i = 3; i < 16*4; i += 4 ) |
617 | { |
618 | /* convert this alpha value to a 3 bit number */ |
619 | int svalue; |
620 | int value = (int)((uncompressed[i] - a1) * scale_me); |
621 | svalue = swizzle8[ value&7 ]; |
622 | /* OK, store this value, start with the 1st byte */ |
623 | compressed[next_bit >> 3] |= svalue << (next_bit & 7); |
624 | if( (next_bit & 7) > 5 ) |
625 | { |
626 | /* spans 2 bytes, fill in the start of the 2nd byte */ |
627 | compressed[1 + (next_bit >> 3)] |= svalue >> (8 - (next_bit & 7) ); |
628 | } |
629 | next_bit += 3; |
630 | } |
631 | /* done compressing to DXT1 */ |
632 | } |
633 | |