1 /* -----------------------------------------------------------------------------
3 Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
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:
13 The above copyright notice and this permission notice shall be included
14 in all copies or substantial portions of the Software.
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.
24 -------------------------------------------------------------------------- */
26 #include "OSGSquish.h"
27 #include "colourset.h"
30 #include "clusterfit.h"
31 #include "colourblock.h"
33 #include "singlecolourfit.h"
37 static int FixFlags( int flags
)
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
;
46 if( method
!= kDxt3
&& method
!= kDxt5
)
48 if( fit
!= kColourRangeFit
)
49 fit
= kColourClusterFit
;
50 if( metric
!= kColourMetricUniform
)
51 metric
= kColourMetricPerceptual
;
54 return method
| fit
| metric
| extra
;
57 void Compress( u8
const* rgba
, void* block
, int flags
)
59 // compress with full mask
60 CompressMasked( rgba
, 0xffff, block
, flags
);
63 void CompressMasked( u8
const* rgba
, int mask
, void* block
, int flags
)
66 flags
= FixFlags( flags
);
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;
74 // create the minimal point set
75 ColourSet
colours( rgba
, mask
, flags
);
77 // check the compression type and compress colour
78 if( colours
.GetCount() == 1 )
80 // always do a single colour fit
81 SingleColourFit
fit( &colours
, flags
);
82 fit
.Compress( colourBlock
);
84 else if( ( flags
& kColourRangeFit
) != 0 || colours
.GetCount() == 0 )
87 RangeFit
fit( &colours
, flags
);
88 fit
.Compress( colourBlock
);
92 // default to a cluster fit (could be iterative or not)
93 ClusterFit
fit( &colours
, flags
);
94 fit
.Compress( colourBlock
);
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
);
104 void Decompress( u8
* rgba
, void const* block
, int flags
)
107 flags
= FixFlags( flags
);
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;
116 DecompressColour( rgba
, colourBlock
, ( flags
& kDxt1
) != 0 );
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
);
125 int GetStorageRequirements( int width
, int height
, int flags
)
128 flags
= FixFlags( flags
);
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
;
136 void CompressImage( u8
const* rgba
, int width
, int height
, void* blocks
, int flags
)
139 flags
= FixFlags( flags
);
141 // initialise the block output
142 u8
* targetBlock
= reinterpret_cast< u8
* >( blocks
);
143 int bytesPerBlock
= ( ( flags
& kDxt1
) != 0 ) ? 8 : 16;
146 for( int y
= 0; y
< height
; y
+= 4 )
148 for( int x
= 0; x
< width
; x
+= 4 )
150 // build the 4x4 block of pixels
152 u8
* targetPixel
= sourceRgba
;
154 for( int py
= 0; py
< 4; ++py
)
156 for( int px
= 0; px
< 4; ++px
)
158 // get the source pixel in the image
162 // enable if we're in the image
163 if( sx
< width
&& sy
< height
)
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
++;
171 mask
|= ( 1 << ( 4*py
+ px
) );
175 // skip this pixel as its outside the image
181 // compress it into the output
182 CompressMasked( sourceRgba
, mask
, targetBlock
, flags
);
185 targetBlock
+= bytesPerBlock
;
190 void CompressImage( u8
const* rgb
, u8 a
, int width
, int height
, void* blocks
, int flags
)
193 flags
= FixFlags( flags
);
195 // initialise the block output
196 u8
* targetBlock
= reinterpret_cast< u8
* >( blocks
);
197 int bytesPerBlock
= ( ( flags
& kDxt1
) != 0 ) ? 8 : 16;
200 for( int y
= 0; y
< height
; y
+= 4 )
202 for( int x
= 0; x
< width
; x
+= 4 )
204 // build the 4x4 block of pixels
206 u8
* targetPixel
= sourceRgba
;
208 for( int py
= 0; py
< 4; ++py
)
210 for( int px
= 0; px
< 4; ++px
)
212 // get the source pixel in the image
216 // enable if we're in the image
217 if( sx
< width
&& sy
< height
)
219 // copy the rgba value
220 u8
const* sourcePixel
= rgb
+ 3*( width
*sy
+ sx
);
221 for( int i
= 0; i
< 3; ++i
)
222 *targetPixel
++ = *sourcePixel
++;
226 mask
|= ( 1 << ( 4*py
+ px
) );
230 // skip this pixel as its outside the image
236 // compress it into the output
237 CompressMasked( sourceRgba
, mask
, targetBlock
, flags
);
240 targetBlock
+= bytesPerBlock
;
245 void DecompressImage( u8
* rgba
, int width
, int height
, void const* blocks
, int flags
)
248 flags
= FixFlags( flags
);
250 // initialise the block input
251 u8
const* sourceBlock
= reinterpret_cast< u8
const* >( blocks
);
252 int bytesPerBlock
= ( ( flags
& kDxt1
) != 0 ) ? 8 : 16;
255 for( int y
= 0; y
< height
; y
+= 4 )
257 for( int x
= 0; x
< width
; x
+= 4 )
259 // decompress the block
261 Decompress( targetRgba
, sourceBlock
, flags
);
263 // write the decompressed pixels to the correct image locations
264 u8
const* sourcePixel
= targetRgba
;
265 for( int py
= 0; py
< 4; ++py
)
267 for( int px
= 0; px
< 4; ++px
)
269 // get the target location
272 if( sx
< width
&& sy
< height
)
274 u8
* targetPixel
= rgba
+ 4*( width
*sy
+ sx
);
276 // copy the rgba value
277 for( int i
= 0; i
< 4; ++i
)
278 *targetPixel
++ = *sourcePixel
++;
282 // skip this pixel as its outside the image
289 sourceBlock
+= bytesPerBlock
;
294 } // namespace squish