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