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