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