Merge pull request #4852 from FernetMenta/aefixes
[vuplus_xbmc] / lib / libsquish / rangefit.cpp
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