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