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 <stdio.h>
27 #include <stdint.h>
28
29 #include "squish.h"
30 #include "colourset.h"
31 #include "maths.h"
32 #include "rangefit.h"
33 #include "clusterfit.h"
34 #include "colourblock.h"
35 #include "alpha.h"
36 #include "singlecolourfit.h"
37 #include "singlecolourfitfast.h"
38 #include "twocolourfitfast.h"
39 #include <wx/thread.h>
40
41 extern bool g_throttle_squish;
42
43 namespace squish {
44
FixFlags(int flags)45 static int FixFlags( int flags )
46 {
47 // grab the flag bits
48 int method = flags & ( kDxt1 | kDxt3 | kDxt5 );
49 int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit );
50 int metric = flags & ( kColourMetricPerceptual | kColourMetricUniform );
51 int extra = flags & kWeightColourByAlpha;
52
53 // set defaults
54 if( method != kDxt3 && method != kDxt5 )
55 method = kDxt1;
56 if( fit != kColourRangeFit && fit != kColourIterativeClusterFit)
57 fit = kColourClusterFit;
58 if( metric != kColourMetricUniform )
59 metric = kColourMetricPerceptual;
60
61 // done
62 return method | fit | metric | extra;
63 }
64
Compress_dxt1(u8 const * rgba,void * block,int flags)65 void Compress_dxt1( u8 const* rgba, void* block, int flags )
66 {
67 // get the block locations
68 void* colourBlock = block;
69
70 // create the minimal point set
71 ColourSet colours( rgba, flags );
72
73 // check the compression type and compress colour
74
75 if( colours.GetCount() == 1)
76 {
77 // always do a single colour fit
78 SingleColourFitFast fit( &colours, flags );
79 fit.Compress3( colourBlock );
80 }
81 else if( colours.GetCount() == 2)
82 {
83 TwoColourFitFast fit( &colours, flags );
84 fit.Compress3( colourBlock );
85 }
86 else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() <= 4 )
87 {
88 // do a range fit
89 RangeFit fit( &colours, flags );
90 fit.Compress3( colourBlock );
91 }
92 else
93 {
94 // default to a cluster fit (could be iterative or not)
95 ClusterFit fit( &colours, flags );
96 fit.Compress3( colourBlock );
97 }
98 }
99
Compress(u8 const * rgba,void * block,int flags)100 void Compress( u8 const* rgba, void* block, int flags )
101 {
102 // compress with full mask
103 CompressMasked( rgba, 0xffff, block, flags );
104 }
105
CompressMasked(u8 const * rgba,int mask,void * block,int flags)106 void CompressMasked( u8 const* rgba, int mask, void* block, int flags )
107 {
108 // fix any bad flags
109 flags = FixFlags( flags );
110
111 // get the block locations
112 void* colourBlock = block;
113 void* alphaBock = block;
114 if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
115 colourBlock = reinterpret_cast< u8* >( block ) + 8;
116
117 // create the minimal point set
118 ColourSet colours( rgba, mask, flags );
119
120 // check the compression type and compress colour
121 if( colours.GetCount() == 1 )
122 {
123 // always do a single colour fit
124 SingleColourFit fit( &colours, flags );
125 fit.Compress( colourBlock );
126 }
127 else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 )
128 {
129 // do a range fit
130 RangeFit fit( &colours, flags );
131 fit.Compress( colourBlock );
132 }
133 else
134 {
135 // default to a cluster fit (could be iterative or not)
136 ClusterFit fit( &colours, flags );
137 fit.Compress( colourBlock );
138 }
139
140 // compress alpha separately if necessary
141 if( ( flags & kDxt3 ) != 0 )
142 CompressAlphaDxt3( rgba, mask, alphaBock );
143 else if( ( flags & kDxt5 ) != 0 )
144 CompressAlphaDxt5( rgba, mask, alphaBock );
145 }
146
Decompress(u8 * rgba,void const * block,int flags)147 void Decompress( u8* rgba, void const* block, int flags )
148 {
149 // fix any bad flags
150 flags = FixFlags( flags );
151
152 // get the block locations
153 void const* colourBlock = block;
154 void const* alphaBock = block;
155 if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
156 colourBlock = reinterpret_cast< u8 const* >( block ) + 8;
157
158 // decompress colour
159 DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
160
161 // decompress alpha separately if necessary
162 if( ( flags & kDxt3 ) != 0 )
163 DecompressAlphaDxt3( rgba, alphaBock );
164 else if( ( flags & kDxt5 ) != 0 )
165 DecompressAlphaDxt5( rgba, alphaBock );
166 }
167
GetStorageRequirements(int width,int height,int flags)168 int GetStorageRequirements( int width, int height, int flags )
169 {
170 // fix any bad flags
171 flags = FixFlags( flags );
172
173 // compute the storage requirements
174 int blockcount = ( ( width + 3 )/4 ) * ( ( height + 3 )/4 );
175 int blocksize = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
176 return blockcount*blocksize;
177 }
178
CompressImage(u8 const * rgba,int width,int height,void * blocks,int flags)179 void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags )
180 {
181 // fix any bad flags
182 flags = FixFlags( flags );
183
184 // initialise the block output
185 u8* targetBlock = reinterpret_cast< u8* >( blocks );
186 int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
187
188 // loop over blocks
189 for( int y = 0; y < height; y += 4 )
190 {
191 for( int x = 0; x < width; x += 4 )
192 {
193 // build the 4x4 block of pixels
194 u8 sourceRgba[16*4];
195 u8* targetPixel = sourceRgba;
196 int mask = 0;
197 for( int py = 0; py < 4; ++py )
198 {
199 for( int px = 0; px < 4; ++px )
200 {
201 // get the source pixel in the image
202 int sx = x + px;
203 int sy = y + py;
204
205 // enable if we're in the image
206 if( sx < width && sy < height )
207 {
208 // copy the rgba value
209 u8 const* sourcePixel = rgba + 4*( width*sy + sx );
210 for( int i = 0; i < 4; ++i )
211 *targetPixel++ = *sourcePixel++;
212
213 // enable this pixel
214 mask |= ( 1 << ( 4*py + px ) );
215 }
216 else
217 {
218 // skip this pixel as its outside the image
219 targetPixel += 4;
220 }
221 }
222 }
223
224 // compress it into the output
225 CompressMasked( sourceRgba, mask, targetBlock, flags );
226
227 // advance
228 targetBlock += bytesPerBlock;
229 }
230 }
231 }
232
CompressImageRGB(u8 const * rgb,int width,int height,void * blocks,int flags)233 void CompressImageRGB( u8 const* rgb, int width, int height, void* blocks, int flags )
234 {
235 // fix any bad flags
236 flags = FixFlags( flags );
237
238 // initialise the block output
239 u8* targetBlock = reinterpret_cast< u8* >( blocks );
240 int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
241
242 // loop over blocks
243 for( int y = 0; y < height; y += 4 )
244 {
245 for( int x = 0; x < width; x += 4 )
246 {
247 // build the 4x4 block of pixels
248 u8 sourceRgba[16*4];
249 u8* targetPixel = sourceRgba;
250 int mask = 0;
251 for( int py = 0; py < 4; ++py )
252 {
253 for( int px = 0; px < 4; ++px )
254 {
255 // get the source pixel in the image
256 int sx = x + px;
257 int sy = y + py;
258
259 // enable if we're in the image
260 if( sx < width && sy < height )
261 {
262 // copy the rgba value
263 u8 const* sourcePixel = rgb + 3*( width*sy + sx );
264 for( int i = 0; i < 3; ++i )
265 *targetPixel++ = *sourcePixel++;
266 *targetPixel++ = 255;
267
268 // enable this pixel
269 mask |= ( 1 << ( 4*py + px ) );
270 }
271 else
272 {
273 // skip this pixel as its outside the image
274 targetPixel += 4;
275 }
276 }
277 }
278
279 // compress it into the output
280 CompressMasked( sourceRgba, mask, targetBlock, flags );
281
282 // advance
283 targetBlock += bytesPerBlock;
284 }
285 }
286 }
287
CompressImageRGBpow2_Flatten_Throttle_Abort(u8 const * rgb,int width,int height,void * blocks,int flags,bool b_flatten,void (* throttle)(void *),void * throttle_data,volatile bool & b_abort)288 void CompressImageRGBpow2_Flatten_Throttle_Abort( u8 const* rgb, int width, int height, void* blocks, int flags,
289 bool b_flatten, void (*throttle)(void*), void *throttle_data, volatile bool &b_abort )
290 {
291 // fix any bad flags
292 flags = FixFlags( flags );
293
294 // initialise the block output
295 u8* targetBlock = reinterpret_cast< u8* >( blocks );
296 int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
297
298 u8 r_flat_mask = 0xff;
299 u8 g_flat_mask = 0xff;
300 u8 b_flat_mask = 0xff;
301
302 if(b_flatten){
303 r_flat_mask = 0xf8;
304 g_flat_mask = 0xfc;
305 b_flat_mask = 0xf8;
306 }
307
308 // loop over blocks
309 double tt = 0;
310
311 int bw = std::min(width, 4);
312 int bh = std::min(height, 4);
313
314 for( int y = 0; y < height; y += 4 )
315 {
316 for( int x = 0; x < width; x += 4 )
317 {
318 // build the 4x4 block of pixels
319 u8 sourceRgba[16*4];
320 u8* targetPixel = sourceRgba;
321
322 for( int py = 0; py < 4; ++py )
323 {
324 for( int px = 0; px < 4; ++px )
325 {
326 // get the source pixel in the image
327 int sx = x + (px % bw);
328 int sy = y + (py % bh);
329
330 // copy the rgba value
331 u8 const* sourcePixel = rgb + 3*( width*sy + sx );
332
333 *targetPixel++ = sourcePixel[0] & r_flat_mask;
334 *targetPixel++ = sourcePixel[1] & g_flat_mask;
335 *targetPixel++ = sourcePixel[2] & b_flat_mask;
336 *targetPixel++ = 255;
337 }
338 }
339
340 // compress it into the output
341 Compress_dxt1( sourceRgba, targetBlock, flags );
342 // Compress( sourceRgba, targetBlock, flags );
343
344 // advance
345 targetBlock += bytesPerBlock;
346 }
347 if( throttle )
348 throttle(throttle_data);
349
350 if( b_abort)
351 break;
352 }
353 }
354
355
DecompressImage(u8 * rgba,int width,int height,void const * blocks,int flags)356 void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags )
357 {
358 // fix any bad flags
359 flags = FixFlags( flags );
360
361 // initialise the block input
362 u8 const* sourceBlock = reinterpret_cast< u8 const* >( blocks );
363 int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
364
365 // loop over blocks
366 for( int y = 0; y < height; y += 4 )
367 {
368 for( int x = 0; x < width; x += 4 )
369 {
370 // decompress the block
371 u8 targetRgba[4*16];
372 Decompress( targetRgba, sourceBlock, flags );
373
374 // write the decompressed pixels to the correct image locations
375 u8 const* sourcePixel = targetRgba;
376 for( int py = 0; py < 4; ++py )
377 {
378 for( int px = 0; px < 4; ++px )
379 {
380 // get the target location
381 int sx = x + px;
382 int sy = y + py;
383 if( sx < width && sy < height )
384 {
385 u8* targetPixel = rgba + 4*( width*sy + sx );
386
387 // copy the rgba value
388 for( int i = 0; i < 4; ++i )
389 *targetPixel++ = *sourcePixel++;
390 }
391 else
392 {
393 // skip this pixel as its outside the image
394 sourcePixel += 4;
395 }
396 }
397 }
398
399 // advance
400 sourceBlock += bytesPerBlock;
401 }
402 }
403 }
404
405 } // namespace squish
406