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