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 "squish.h"
27 #include "colourset.h"
28 #include "maths.h"
29 #include "rangefit.h"
30 #include "clusterfit.h"
31 #include "colourblock.h"
32 #include "alpha.h"
33 #include "singlecolourfit.h"
34 
35 namespace squish {
36 
FixFlags(int flags)37 static int FixFlags( int flags )
38 {
39 	// grab the flag bits
40 	int method = flags & ( kDxt1 | kDxt3 | kDxt5 );
41 	int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit );
42 	int extra = flags & kWeightColourByAlpha;
43 
44 	// set defaults
45 	if( method != kDxt3 && method != kDxt5 )
46 		method = kDxt1;
47 	if( fit != kColourRangeFit && fit != kColourIterativeClusterFit )
48 		fit = kColourClusterFit;
49 
50 	// done
51 	return method | fit | extra;
52 }
53 
CompressMasked(u8 const * rgba,int mask,void * block,int flags,float * metric)54 void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric )
55 {
56 	// fix any bad flags
57 	flags = FixFlags( flags );
58 
59 	// get the block locations
60 	void* colourBlock = block;
61 	void* alphaBock = block;
62 	if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
63 		colourBlock = reinterpret_cast< u8* >( block ) + 8;
64 
65 	// create the minimal point set
66 	ColourSet colours( rgba, mask, flags );
67 
68 	// check the compression type and compress colour
69 	if( colours.GetCount() == 1 )
70 	{
71 		// always do a single colour fit
72 		SingleColourFit fit( &colours, flags );
73 		fit.Compress( colourBlock );
74 	}
75 	else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 )
76 	{
77 		// do a range fit
78 		RangeFit fit( &colours, flags, metric );
79 		fit.Compress( colourBlock );
80 	}
81 	else
82 	{
83 		// default to a cluster fit (could be iterative or not)
84 		ClusterFit fit( &colours, flags, metric );
85 		fit.Compress( colourBlock );
86 	}
87 
88 	// compress alpha separately if necessary
89 	if( ( flags & kDxt3 ) != 0 )
90 		CompressAlphaDxt3( rgba, mask, alphaBock );
91 	else if( ( flags & kDxt5 ) != 0 )
92 		CompressAlphaDxt5( rgba, mask, alphaBock );
93 }
94 
Decompress(u8 * rgba,void const * block,int flags)95 void Decompress( u8* rgba, void const* block, int flags )
96 {
97 	// fix any bad flags
98 	flags = FixFlags( flags );
99 
100 	// get the block locations
101 	void const* colourBlock = block;
102 	void const* alphaBock = block;
103 	if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
104 		colourBlock = reinterpret_cast< u8 const* >( block ) + 8;
105 
106 	// decompress colour
107 	DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
108 
109 	// decompress alpha separately if necessary
110 	if( ( flags & kDxt3 ) != 0 )
111 		DecompressAlphaDxt3( rgba, alphaBock );
112 	else if( ( flags & kDxt5 ) != 0 )
113 		DecompressAlphaDxt5( rgba, alphaBock );
114 }
115 
GetStorageRequirements(int width,int height,int flags)116 int GetStorageRequirements( int width, int height, int flags )
117 {
118 	// fix any bad flags
119 	flags = FixFlags( flags );
120 
121 	// compute the storage requirements
122 	int blockcount = ( ( width + 3 )/4 ) * ( ( height + 3 )/4 );
123 	int blocksize = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
124 	return blockcount*blocksize;
125 }
126 
CompressImage(u8 const * rgba,int width,int height,void * blocks,int flags,float * metric)127 void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric )
128 {
129 	// fix any bad flags
130 	flags = FixFlags( flags );
131 
132 	// initialise the block output
133 	u8* targetBlock = reinterpret_cast< u8* >( blocks );
134 	int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
135 
136 	// loop over blocks
137 	for( int y = 0; y < height; y += 4 )
138 	{
139 		for( int x = 0; x < width; x += 4 )
140 		{
141 			// build the 4x4 block of pixels
142 			u8 sourceRgba[16*4];
143 			u8* targetPixel = sourceRgba;
144 			int mask = 0;
145 			for( int py = 0; py < 4; ++py )
146 			{
147 				for( int px = 0; px < 4; ++px )
148 				{
149 					// get the source pixel in the image
150 					int sx = x + px;
151 					int sy = y + py;
152 
153 					// enable if we're in the image
154 					if( sx < width && sy < height )
155 					{
156 						// copy the rgba value
157 						u8 const* sourcePixel = rgba + 4*( width*sy + sx );
158 						for( int i = 0; i < 4; ++i )
159 							*targetPixel++ = *sourcePixel++;
160 
161 						// enable this pixel
162 						mask |= ( 1 << ( 4*py + px ) );
163 					}
164 					else
165 					{
166 						// skip this pixel as its outside the image
167 						targetPixel += 4;
168 					}
169 				}
170 			}
171 
172 			// compress it into the output
173 			CompressMasked( sourceRgba, mask, targetBlock, flags, metric );
174 
175 			// advance
176 			targetBlock += bytesPerBlock;
177 		}
178 	}
179 }
180 
DecompressImage(u8 * rgba,int width,int height,void const * blocks,int flags)181 void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags )
182 {
183 	// fix any bad flags
184 	flags = FixFlags( flags );
185 
186 	// initialise the block input
187 	u8 const* sourceBlock = reinterpret_cast< u8 const* >( blocks );
188 	int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
189 
190 	// loop over blocks
191 	for( int y = 0; y < height; y += 4 )
192 	{
193 		for( int x = 0; x < width; x += 4 )
194 		{
195 			// decompress the block
196 			u8 targetRgba[4*16];
197 			Decompress( targetRgba, sourceBlock, flags );
198 
199 			// write the decompressed pixels to the correct image locations
200 			u8 const* sourcePixel = targetRgba;
201 			for( int py = 0; py < 4; ++py )
202 			{
203 				for( int px = 0; px < 4; ++px )
204 				{
205 					// get the target location
206 					int sx = x + px;
207 					int sy = y + py;
208 					if( sx < width && sy < height )
209 					{
210 						u8* targetPixel = rgba + 4*( width*sy + sx );
211 
212 						// copy the rgba value
213 						for( int i = 0; i < 4; ++i )
214 							*targetPixel++ = *sourcePixel++;
215 					}
216 					else
217 					{
218 						// skip this pixel as its outside the image
219 						sourcePixel += 4;
220 					}
221 				}
222 			}
223 
224 			// advance
225 			sourceBlock += bytesPerBlock;
226 		}
227 	}
228 }
229 
230 } // namespace squish
231