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