1*2f083884Ss.makeev_local /* -----------------------------------------------------------------------------
2*2f083884Ss.makeev_local
3*2f083884Ss.makeev_local Copyright (c) 2006 Simon Brown [email protected]
4*2f083884Ss.makeev_local
5*2f083884Ss.makeev_local Permission is hereby granted, free of charge, to any person obtaining
6*2f083884Ss.makeev_local a copy of this software and associated documentation files (the
7*2f083884Ss.makeev_local "Software"), to deal in the Software without restriction, including
8*2f083884Ss.makeev_local without limitation the rights to use, copy, modify, merge, publish,
9*2f083884Ss.makeev_local distribute, sublicense, and/or sell copies of the Software, and to
10*2f083884Ss.makeev_local permit persons to whom the Software is furnished to do so, subject to
11*2f083884Ss.makeev_local the following conditions:
12*2f083884Ss.makeev_local
13*2f083884Ss.makeev_local The above copyright notice and this permission notice shall be included
14*2f083884Ss.makeev_local in all copies or substantial portions of the Software.
15*2f083884Ss.makeev_local
16*2f083884Ss.makeev_local THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17*2f083884Ss.makeev_local OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*2f083884Ss.makeev_local MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19*2f083884Ss.makeev_local IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20*2f083884Ss.makeev_local CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21*2f083884Ss.makeev_local TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22*2f083884Ss.makeev_local SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23*2f083884Ss.makeev_local
24*2f083884Ss.makeev_local -------------------------------------------------------------------------- */
25*2f083884Ss.makeev_local
26*2f083884Ss.makeev_local #include "rangefit.h"
27*2f083884Ss.makeev_local #include "colourset.h"
28*2f083884Ss.makeev_local #include "colourblock.h"
29*2f083884Ss.makeev_local #include <cfloat>
30*2f083884Ss.makeev_local
31*2f083884Ss.makeev_local namespace squish {
32*2f083884Ss.makeev_local
RangeFit(ColourSet const * colours,int flags)33*2f083884Ss.makeev_local RangeFit::RangeFit( ColourSet const* colours, int flags )
34*2f083884Ss.makeev_local : ColourFit( colours, flags )
35*2f083884Ss.makeev_local {
36*2f083884Ss.makeev_local // initialise the metric
37*2f083884Ss.makeev_local bool perceptual = ( ( m_flags & kColourMetricPerceptual ) != 0 );
38*2f083884Ss.makeev_local if( perceptual )
39*2f083884Ss.makeev_local m_metric = Vec3( 0.2126f, 0.7152f, 0.0722f );
40*2f083884Ss.makeev_local else
41*2f083884Ss.makeev_local m_metric = Vec3( 1.0f );
42*2f083884Ss.makeev_local
43*2f083884Ss.makeev_local // initialise the best error
44*2f083884Ss.makeev_local m_besterror = FLT_MAX;
45*2f083884Ss.makeev_local
46*2f083884Ss.makeev_local // cache some values
47*2f083884Ss.makeev_local int const count = m_colours->GetCount();
48*2f083884Ss.makeev_local Vec3 const* values = m_colours->GetPoints();
49*2f083884Ss.makeev_local float const* weights = m_colours->GetWeights();
50*2f083884Ss.makeev_local
51*2f083884Ss.makeev_local // get the covariance matrix
52*2f083884Ss.makeev_local Sym3x3 covariance = ComputeWeightedCovariance( count, values, weights );
53*2f083884Ss.makeev_local
54*2f083884Ss.makeev_local // compute the principle component
55*2f083884Ss.makeev_local Vec3 principle = ComputePrincipleComponent( covariance );
56*2f083884Ss.makeev_local
57*2f083884Ss.makeev_local // get the min and max range as the codebook endpoints
58*2f083884Ss.makeev_local Vec3 start( 0.0f );
59*2f083884Ss.makeev_local Vec3 end( 0.0f );
60*2f083884Ss.makeev_local if( count > 0 )
61*2f083884Ss.makeev_local {
62*2f083884Ss.makeev_local float min, max;
63*2f083884Ss.makeev_local
64*2f083884Ss.makeev_local // compute the range
65*2f083884Ss.makeev_local start = end = values[0];
66*2f083884Ss.makeev_local min = max = Dot( values[0], principle );
67*2f083884Ss.makeev_local for( int i = 1; i < count; ++i )
68*2f083884Ss.makeev_local {
69*2f083884Ss.makeev_local float val = Dot( values[i], principle );
70*2f083884Ss.makeev_local if( val < min )
71*2f083884Ss.makeev_local {
72*2f083884Ss.makeev_local start = values[i];
73*2f083884Ss.makeev_local min = val;
74*2f083884Ss.makeev_local }
75*2f083884Ss.makeev_local else if( val > max )
76*2f083884Ss.makeev_local {
77*2f083884Ss.makeev_local end = values[i];
78*2f083884Ss.makeev_local max = val;
79*2f083884Ss.makeev_local }
80*2f083884Ss.makeev_local }
81*2f083884Ss.makeev_local }
82*2f083884Ss.makeev_local
83*2f083884Ss.makeev_local // clamp the output to [0, 1]
84*2f083884Ss.makeev_local Vec3 const one( 1.0f );
85*2f083884Ss.makeev_local Vec3 const zero( 0.0f );
86*2f083884Ss.makeev_local start = Min( one, Max( zero, start ) );
87*2f083884Ss.makeev_local end = Min( one, Max( zero, end ) );
88*2f083884Ss.makeev_local
89*2f083884Ss.makeev_local // clamp to the grid and save
90*2f083884Ss.makeev_local Vec3 const grid( 31.0f, 63.0f, 31.0f );
91*2f083884Ss.makeev_local Vec3 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f );
92*2f083884Ss.makeev_local Vec3 const half( 0.5f );
93*2f083884Ss.makeev_local m_start = Truncate( grid*start + half )*gridrcp;
94*2f083884Ss.makeev_local m_end = Truncate( grid*end + half )*gridrcp;
95*2f083884Ss.makeev_local }
96*2f083884Ss.makeev_local
Compress3(void * block)97*2f083884Ss.makeev_local void RangeFit::Compress3( void* block )
98*2f083884Ss.makeev_local {
99*2f083884Ss.makeev_local // cache some values
100*2f083884Ss.makeev_local int const count = m_colours->GetCount();
101*2f083884Ss.makeev_local Vec3 const* values = m_colours->GetPoints();
102*2f083884Ss.makeev_local
103*2f083884Ss.makeev_local // create a codebook
104*2f083884Ss.makeev_local Vec3 codes[3];
105*2f083884Ss.makeev_local codes[0] = m_start;
106*2f083884Ss.makeev_local codes[1] = m_end;
107*2f083884Ss.makeev_local codes[2] = 0.5f*m_start + 0.5f*m_end;
108*2f083884Ss.makeev_local
109*2f083884Ss.makeev_local // match each point to the closest code
110*2f083884Ss.makeev_local u8 closest[16];
111*2f083884Ss.makeev_local float error = 0.0f;
112*2f083884Ss.makeev_local for( int i = 0; i < count; ++i )
113*2f083884Ss.makeev_local {
114*2f083884Ss.makeev_local // find the closest code
115*2f083884Ss.makeev_local float dist = FLT_MAX;
116*2f083884Ss.makeev_local int idx = 0;
117*2f083884Ss.makeev_local for( int j = 0; j < 3; ++j )
118*2f083884Ss.makeev_local {
119*2f083884Ss.makeev_local float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
120*2f083884Ss.makeev_local if( d < dist )
121*2f083884Ss.makeev_local {
122*2f083884Ss.makeev_local dist = d;
123*2f083884Ss.makeev_local idx = j;
124*2f083884Ss.makeev_local }
125*2f083884Ss.makeev_local }
126*2f083884Ss.makeev_local
127*2f083884Ss.makeev_local // save the index
128*2f083884Ss.makeev_local closest[i] = ( u8 )idx;
129*2f083884Ss.makeev_local
130*2f083884Ss.makeev_local // accumulate the error
131*2f083884Ss.makeev_local error += dist;
132*2f083884Ss.makeev_local }
133*2f083884Ss.makeev_local
134*2f083884Ss.makeev_local // save this scheme if it wins
135*2f083884Ss.makeev_local if( error < m_besterror )
136*2f083884Ss.makeev_local {
137*2f083884Ss.makeev_local // remap the indices
138*2f083884Ss.makeev_local u8 indices[16];
139*2f083884Ss.makeev_local m_colours->RemapIndices( closest, indices );
140*2f083884Ss.makeev_local
141*2f083884Ss.makeev_local // save the block
142*2f083884Ss.makeev_local WriteColourBlock3( m_start, m_end, indices, block );
143*2f083884Ss.makeev_local
144*2f083884Ss.makeev_local // save the error
145*2f083884Ss.makeev_local m_besterror = error;
146*2f083884Ss.makeev_local }
147*2f083884Ss.makeev_local }
148*2f083884Ss.makeev_local
Compress4(void * block)149*2f083884Ss.makeev_local void RangeFit::Compress4( void* block )
150*2f083884Ss.makeev_local {
151*2f083884Ss.makeev_local // cache some values
152*2f083884Ss.makeev_local int const count = m_colours->GetCount();
153*2f083884Ss.makeev_local Vec3 const* values = m_colours->GetPoints();
154*2f083884Ss.makeev_local
155*2f083884Ss.makeev_local // create a codebook
156*2f083884Ss.makeev_local Vec3 codes[4];
157*2f083884Ss.makeev_local codes[0] = m_start;
158*2f083884Ss.makeev_local codes[1] = m_end;
159*2f083884Ss.makeev_local codes[2] = ( 2.0f/3.0f )*m_start + ( 1.0f/3.0f )*m_end;
160*2f083884Ss.makeev_local codes[3] = ( 1.0f/3.0f )*m_start + ( 2.0f/3.0f )*m_end;
161*2f083884Ss.makeev_local
162*2f083884Ss.makeev_local // match each point to the closest code
163*2f083884Ss.makeev_local u8 closest[16];
164*2f083884Ss.makeev_local float error = 0.0f;
165*2f083884Ss.makeev_local for( int i = 0; i < count; ++i )
166*2f083884Ss.makeev_local {
167*2f083884Ss.makeev_local // find the closest code
168*2f083884Ss.makeev_local float dist = FLT_MAX;
169*2f083884Ss.makeev_local int idx = 0;
170*2f083884Ss.makeev_local for( int j = 0; j < 4; ++j )
171*2f083884Ss.makeev_local {
172*2f083884Ss.makeev_local float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
173*2f083884Ss.makeev_local if( d < dist )
174*2f083884Ss.makeev_local {
175*2f083884Ss.makeev_local dist = d;
176*2f083884Ss.makeev_local idx = j;
177*2f083884Ss.makeev_local }
178*2f083884Ss.makeev_local }
179*2f083884Ss.makeev_local
180*2f083884Ss.makeev_local // save the index
181*2f083884Ss.makeev_local closest[i] = ( u8 )idx;
182*2f083884Ss.makeev_local
183*2f083884Ss.makeev_local // accumulate the error
184*2f083884Ss.makeev_local error += dist;
185*2f083884Ss.makeev_local }
186*2f083884Ss.makeev_local
187*2f083884Ss.makeev_local // save this scheme if it wins
188*2f083884Ss.makeev_local if( error < m_besterror )
189*2f083884Ss.makeev_local {
190*2f083884Ss.makeev_local // remap the indices
191*2f083884Ss.makeev_local u8 indices[16];
192*2f083884Ss.makeev_local m_colours->RemapIndices( closest, indices );
193*2f083884Ss.makeev_local
194*2f083884Ss.makeev_local // save the block
195*2f083884Ss.makeev_local WriteColourBlock4( m_start, m_end, indices, block );
196*2f083884Ss.makeev_local
197*2f083884Ss.makeev_local // save the error
198*2f083884Ss.makeev_local m_besterror = error;
199*2f083884Ss.makeev_local }
200*2f083884Ss.makeev_local }
201*2f083884Ss.makeev_local
202*2f083884Ss.makeev_local } // namespace squish
203