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 "rangefit.h" |
27 | #include "colourset.h" |
28 | #include "colourblock.h" |
29 | #include <cfloat> |
30 | |
31 | namespace squish { |
32 | |
33 | RangeFit::RangeFit( ColourSet const* colours, int flags, float* metric ) |
34 | : ColourFit( colours, flags ) |
35 | { |
36 | // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f) |
37 | if( metric ) |
38 | m_metric = Vec3( metric[0], metric[1], metric[2] ); |
39 | else |
40 | m_metric = Vec3( 1.0f ); |
41 | |
42 | // initialise the best error |
43 | m_besterror = FLT_MAX; |
44 | |
45 | // cache some values |
46 | int const count = m_colours->GetCount(); |
47 | Vec3 const* values = m_colours->GetPoints(); |
48 | float const* weights = m_colours->GetWeights(); |
49 | |
50 | // get the covariance matrix |
51 | Sym3x3 covariance = ComputeWeightedCovariance( count, values, weights ); |
52 | |
53 | // compute the principle component |
54 | Vec3 principle = ComputePrincipleComponent( covariance ); |
55 | |
56 | // get the min and max range as the codebook endpoints |
57 | Vec3 start( 0.0f ); |
58 | Vec3 end( 0.0f ); |
59 | if( count > 0 ) |
60 | { |
61 | float min, max; |
62 | |
63 | // compute the range |
64 | start = end = values[0]; |
65 | min = max = Dot( values[0], principle ); |
66 | for( int i = 1; i < count; ++i ) |
67 | { |
68 | float val = Dot( values[i], principle ); |
69 | if( val < min ) |
70 | { |
71 | start = values[i]; |
72 | min = val; |
73 | } |
74 | else if( val > max ) |
75 | { |
76 | end = values[i]; |
77 | max = val; |
78 | } |
79 | } |
80 | } |
81 | |
82 | // clamp the output to [0, 1] |
83 | Vec3 const one( 1.0f ); |
84 | Vec3 const zero( 0.0f ); |
85 | start = Min( one, Max( zero, start ) ); |
86 | end = Min( one, Max( zero, end ) ); |
87 | |
88 | // clamp to the grid and save |
89 | Vec3 const grid( 31.0f, 63.0f, 31.0f ); |
90 | Vec3 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f ); |
91 | Vec3 const half( 0.5f ); |
92 | m_start = Truncate( grid*start + half )*gridrcp; |
93 | m_end = Truncate( grid*end + half )*gridrcp; |
94 | } |
95 | |
96 | void RangeFit::Compress3( void* block ) |
97 | { |
98 | // cache some values |
99 | int const count = m_colours->GetCount(); |
100 | Vec3 const* values = m_colours->GetPoints(); |
101 | |
102 | // create a codebook |
103 | Vec3 codes[3]; |
104 | codes[0] = m_start; |
105 | codes[1] = m_end; |
106 | codes[2] = 0.5f*m_start + 0.5f*m_end; |
107 | |
108 | // match each point to the closest code |
109 | u8 closest[16]; |
110 | float error = 0.0f; |
111 | for( int i = 0; i < count; ++i ) |
112 | { |
113 | // find the closest code |
114 | float dist = FLT_MAX; |
115 | int idx = 0; |
116 | for( int j = 0; j < 3; ++j ) |
117 | { |
118 | float d = LengthSquared( m_metric*( values[i] - codes[j] ) ); |
119 | if( d < dist ) |
120 | { |
121 | dist = d; |
122 | idx = j; |
123 | } |
124 | } |
125 | |
126 | // save the index |
127 | closest[i] = ( u8 )idx; |
128 | |
129 | // accumulate the error |
130 | error += dist; |
131 | } |
132 | |
133 | // save this scheme if it wins |
134 | if( error < m_besterror ) |
135 | { |
136 | // remap the indices |
137 | u8 indices[16]; |
138 | m_colours->RemapIndices( closest, indices ); |
139 | |
140 | // save the block |
141 | WriteColourBlock3( m_start, m_end, indices, block ); |
142 | |
143 | // save the error |
144 | m_besterror = error; |
145 | } |
146 | } |
147 | |
148 | void RangeFit::Compress4( void* block ) |
149 | { |
150 | // cache some values |
151 | int const count = m_colours->GetCount(); |
152 | Vec3 const* values = m_colours->GetPoints(); |
153 | |
154 | // create a codebook |
155 | Vec3 codes[4]; |
156 | codes[0] = m_start; |
157 | codes[1] = m_end; |
158 | codes[2] = ( 2.0f/3.0f )*m_start + ( 1.0f/3.0f )*m_end; |
159 | codes[3] = ( 1.0f/3.0f )*m_start + ( 2.0f/3.0f )*m_end; |
160 | |
161 | // match each point to the closest code |
162 | u8 closest[16]; |
163 | float error = 0.0f; |
164 | for( int i = 0; i < count; ++i ) |
165 | { |
166 | // find the closest code |
167 | float dist = FLT_MAX; |
168 | int idx = 0; |
169 | for( int j = 0; j < 4; ++j ) |
170 | { |
171 | float d = LengthSquared( m_metric*( values[i] - codes[j] ) ); |
172 | if( d < dist ) |
173 | { |
174 | dist = d; |
175 | idx = j; |
176 | } |
177 | } |
178 | |
179 | // save the index |
180 | closest[i] = ( u8 )idx; |
181 | |
182 | // accumulate the error |
183 | error += dist; |
184 | } |
185 | |
186 | // save this scheme if it wins |
187 | if( error < m_besterror ) |
188 | { |
189 | // remap the indices |
190 | u8 indices[16]; |
191 | m_colours->RemapIndices( closest, indices ); |
192 | |
193 | // save the block |
194 | WriteColourBlock4( m_start, m_end, indices, block ); |
195 | |
196 | // save the error |
197 | m_besterror = error; |
198 | } |
199 | } |
200 | |
201 | } // namespace squish |
202 | |