KiCad PCB EDA Suite
color4d.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KICAD, a free EDA CAD application.
3  *
4  * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
5  * Copyright (C) 2012 Kicad Developers, see change_log.txt for contributors.
6  *
7  * Color class
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
27 #include <map>
28 
29 #include <gal/color4d.h>
30 
31 using namespace KIGFX;
32 
34 {
35  if( aColor <= UNSPECIFIED_COLOR || aColor >= NBCOLORS )
36  {
37  *this = COLOR4D::UNSPECIFIED;
38  return;
39  }
40 
41  r = g_ColorRefs[aColor].m_Red / 255.0;
42  g = g_ColorRefs[aColor].m_Green / 255.0;
43  b = g_ColorRefs[aColor].m_Blue / 255.0;
44  a = 1.0;
45 }
46 
47 
48 #ifdef WX_COMPATIBILITY
49  COLOR4D::COLOR4D( const wxColour& aColor )
50  {
51  r = aColor.Red() / 255.0;
52  g = aColor.Green() / 255.0;
53  b = aColor.Blue() / 255.0;
54  a = aColor.Alpha() / 255.0;
55  }
56 
57 
58  bool COLOR4D::SetFromWxString( const wxString& aColorString )
59  {
60  wxColour c;
61 
62  if( c.Set( aColorString ) )
63  {
64  r = c.Red() / 255.0;
65  g = c.Green() / 255.0;
66  b = c.Blue() / 255.0;
67  a = c.Alpha() / 255.0;
68 
69  return true;
70  }
71 
72  return false;
73  }
74 
75 
76  wxString COLOR4D::ToWxString( long flags )
77  {
78  wxColour c = ToColour();
79  return c.GetAsString( flags );
80  }
81 
82 
83  COLOR4D COLOR4D::LegacyMix( COLOR4D aColor ) const
84  {
85  COLOR4D candidate;
86 
87  // Blend the two colors (i.e. OR the RGB values)
88  candidate.r = ( (unsigned)( 255.0 * r ) | (unsigned)( 255.0 * aColor.r ) ) / 255.0,
89  candidate.g = ( (unsigned)( 255.0 * g ) | (unsigned)( 255.0 * aColor.g ) ) / 255.0,
90  candidate.b = ( (unsigned)( 255.0 * b ) | (unsigned)( 255.0 * aColor.b ) ) / 255.0,
91  candidate.a = 1.0;
92 
93  return candidate;
94  }
95 
96 
97  COLOR4D& COLOR4D::SetToLegacyHighlightColor()
98  {
99  EDA_COLOR_T legacyColor = GetNearestLegacyColor( *this );
100  EDA_COLOR_T highlightColor = g_ColorRefs[legacyColor].m_LightColor;
101 
102  r = g_ColorRefs[highlightColor].m_Red / 255.0;
103  g = g_ColorRefs[highlightColor].m_Green / 255.0;
104  b = g_ColorRefs[highlightColor].m_Blue / 255.0;
105  a = 1.0;
106 
107  return *this;
108  }
109 
110 
111  COLOR4D& COLOR4D::SetToNearestLegacyColor()
112  {
113  EDA_COLOR_T legacyColor = GetNearestLegacyColor( *this );
114 
115  r = g_ColorRefs[legacyColor].m_Red / 255.0;
116  g = g_ColorRefs[legacyColor].m_Green / 255.0;
117  b = g_ColorRefs[legacyColor].m_Blue / 255.0;
118  a = 1.0;
119 
120  return *this;
121  }
122 
123 
124  unsigned int COLOR4D::ToU32() const
125  {
126  return ToColour().GetRGB();
127  }
128 
129 
130  void COLOR4D::FromU32( unsigned int aPackedColor )
131  {
132  wxColour c;
133  c.SetRGB( aPackedColor );
134  r = c.Red() / 255.0;
135  g = c.Green() / 255.0;
136  b = c.Blue() / 255.0;
137  a = c.Alpha() / 255.0;
138  }
139 
140 
141  EDA_COLOR_T COLOR4D::GetNearestLegacyColor( COLOR4D &aColor )
142  {
143  // Cache layer implemented here, because all callers are using wxColour
144  static std::map< unsigned int, unsigned int > nearestCache;
145  static double hues[NBCOLORS];
146  static double values[NBCOLORS];
147 
148  unsigned int colorInt = aColor.ToU32();
149 
150  auto search = nearestCache.find( colorInt );
151 
152  if( search != nearestCache.end() )
153  return static_cast<EDA_COLOR_T>( search->second );
154 
155  // First use ColorFindNearest to check for exact matches
156  EDA_COLOR_T nearest = ColorFindNearest( aColor.r * 255.0, aColor.g * 255.0, aColor.b * 255.0 );
157 
158  if( COLOR4D( nearest ) == aColor )
159  {
160  nearestCache.insert( std::pair< unsigned int, unsigned int >(
161  colorInt, static_cast<unsigned int>( nearest ) ) );
162  return nearest;
163  }
164 
165  // If not, use hue and value to match.
166  // Hue will be NAN for grayscale colors.
167  // The legacy color palette is a grid across hue and value.
168  // We can exploit that to find a good match -- hue is most apparent to the user.
169  // So, first we determine the closest hue match, and then the closest value from that
170  // "grid row" in the legacy palette.
171 
172  double h, s, v;
173  aColor.ToHSV( h, s, v );
174 
175  double minDist = 360.0;
176  double legacyHue = 0.0;
177 
178  if( std::isnan( h ) )
179  {
180  legacyHue = NAN;
181  }
182  else
183  {
184  for( EDA_COLOR_T candidate = ::BLACK;
185  candidate < NBCOLORS; candidate = NextColor( candidate ) )
186  {
187  double ch, cs, cv;
188 
189  if( hues[candidate] == 0.0 && values[candidate] == 0.0 )
190  {
191  COLOR4D candidate4d( candidate );
192 
193  candidate4d.ToHSV( ch, cs, cv );
194 
195  values[candidate] = cv;
196  // Set the hue to non-zero for black so that we won't do this more than once
197  hues[candidate] = ( cv == 0.0 ) ? 1.0 : ch;
198  }
199  else
200  {
201  ch = hues[candidate];
202  cv = values[candidate];
203  cv = 0.0;
204  }
205 
206  if( fabs( ch - h ) < minDist )
207  {
208  minDist = fabs( ch - h );
209  legacyHue = ch;
210  }
211  }
212  }
213 
214  // Now we have the desired hue; let's find the nearest value
215  minDist = 1.0;
216  for( EDA_COLOR_T candidate = ::BLACK;
217  candidate < NBCOLORS; candidate = NextColor( candidate ) )
218  {
219  // If the target hue is NAN, we didn't extract the value for any colors above
220  if( std::isnan( legacyHue ) )
221  {
222  double ch, cs, cv;
223  COLOR4D candidate4d( candidate );
224  candidate4d.ToHSV( ch, cs, cv );
225  values[candidate] = cv;
226  hues[candidate] = ( cv == 0.0 ) ? 1.0 : ch;
227  }
228 
229  if( ( std::isnan( legacyHue ) != std::isnan( hues[candidate] ) ) || hues[candidate] != legacyHue )
230  continue;
231 
232  if( fabs( values[candidate] - v ) < minDist )
233  {
234  minDist = fabs( values[candidate] - v );
235  nearest = candidate;
236  }
237  }
238 
239  nearestCache.insert( std::pair< unsigned int, unsigned int >(
240  colorInt, static_cast<unsigned int>( nearest ) ) );
241 
242  return nearest;
243  }
244 #endif
245 
246 namespace KIGFX {
247 
248 const bool operator==( const COLOR4D& lhs, const COLOR4D& rhs )
249 {
250  return lhs.a == rhs.a && lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b;
251 }
252 
253 
254 const bool operator!=( const COLOR4D& lhs, const COLOR4D& rhs )
255 {
256  return !( lhs == rhs );
257 }
258 
259 }
260 
261 
262 void COLOR4D::ToHSV( double& aOutH, double& aOutS, double& aOutV ) const
263 {
264  double min, max, delta;
265 
266  min = r < g ? r : g;
267  min = min < b ? min : b;
268 
269  max = r > g ? r : g;
270  max = max > b ? max : b;
271 
272  aOutV = max; // v
273  delta = max - min;
274 
275  if( max > 0.0 )
276  {
277  aOutS = ( delta / max ); // s
278  }
279  else
280  {
281  // r = g = b = 0 // s = 0, v is undefined
282  aOutS = 0.0;
283  aOutH = NAN; // its now undefined
284  return;
285  }
286 
287  if( r >= max ) // > is bogus, just keeps compiler happy
288  aOutH = ( g - b ) / delta; // between yellow & magenta
289  else if( g >= max )
290  aOutH = 2.0 + ( b - r ) / delta; // between cyan & yellow
291  else
292  aOutH = 4.0 + ( r - g ) / delta; // between magenta & cyan
293 
294  aOutH *= 60.0; // degrees
295 
296  if( aOutH < 0.0 )
297  aOutH += 360.0;
298 }
299 
300 
301 void COLOR4D::FromHSV( double aInH, double aInS, double aInV )
302 {
303  double hh, p, q, t, ff;
304  long i;
305 
306  if( aInS <= 0.0 ) // < is bogus, just shuts up warnings
307  {
308  r = aInV;
309  g = aInV;
310  b = aInV;
311  return;
312  }
313 
314  hh = aInH;
315 
316  if( hh >= 360.0 )
317  hh = 0.0;
318 
319  hh /= 60.0;
320 
321  i = (long) hh;
322  ff = hh - i;
323 
324  p = aInV * ( 1.0 - aInS );
325  q = aInV * ( 1.0 - ( aInS * ff ) );
326  t = aInV * ( 1.0 - ( aInS * ( 1.0 - ff ) ) );
327 
328  switch( i )
329  {
330  case 0:
331  r = aInV;
332  g = t;
333  b = p;
334  break;
335 
336  case 1:
337  r = q;
338  g = aInV;
339  b = p;
340  break;
341 
342  case 2:
343  r = p;
344  g = aInV;
345  b = t;
346  break;
347 
348  case 3:
349  r = p;
350  g = q;
351  b = aInV;
352  break;
353 
354  case 4:
355  r = t;
356  g = p;
357  b = aInV;
358  break;
359 
360  case 5:
361  default:
362  r = aInV;
363  g = p;
364  b = q;
365  break;
366  }
367 }
368 
369 
370 COLOR4D& COLOR4D::Saturate( double aFactor )
371 {
372  double h, s, v;
373 
374  ToHSV( h, s, v );
375  FromHSV( h, aFactor, 1.0 );
376 
377  return *this;
378 }
379 
380 const COLOR4D COLOR4D::UNSPECIFIED( 0, 0, 0, 0 );
381 const COLOR4D COLOR4D::WHITE( 1, 1, 1, 1 );
382 const COLOR4D COLOR4D::BLACK( 0, 0, 0, 1 );
EDA_COLOR_T
NOTE: EDA_COLOR_T is deprecated and is kept around for compatibility with legacy canvas.
Definition: colors.h:42
EDA_COLOR_T ColorFindNearest(const wxColour &aColor)
Find the nearest color match.
Definition: colors.cpp:96
unsigned char m_Red
Definition: colors.h:146
Class CAIRO_GAL is the cairo implementation of the graphics abstraction layer.
Definition: class_module.h:56
COLOR4D & Saturate(double aFactor)
Saturates the color to a given factor (in HSV model)
Definition: color4d.cpp:370
unsigned char m_Green
Definition: colors.h:145
void ToHSV(double &aOutH, double &aOutS, double &aOutV) const
Function ToHSV() Converts current color (stored in RGB) to HSV format.
Definition: color4d.cpp:262
static const COLOR4D BLACK
Definition: color4d.h:291
double g
Green component.
Definition: color4d.h:282
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition: color4d.h:287
static const int delta[8][2]
Definition: solve.cpp:112
double b
Blue component.
Definition: color4d.h:283
double a
Alpha component.
Definition: color4d.h:284
unsigned char m_Blue
Definition: colors.h:144
const StructColors g_ColorRefs[NBCOLORS]
The predefined colors used in KiCad.
Definition: colors.cpp:37
static const COLOR4D WHITE
Definition: color4d.h:290
void FromHSV(double aInH, double aInS, double aInV)
Function FromHSV() Changes currently used color to the one given by hue, saturation and value paramet...
Definition: color4d.cpp:301
#define max(a, b)
Definition: auxiliary.h:86
EDA_COLOR_T m_LightColor
Definition: colors.h:150
const bool operator==(const COLOR4D &lhs, const COLOR4D &rhs)
Equality operator, are two colors equal.
Definition: color4d.cpp:248
double r
Red component.
Definition: color4d.h:281
const bool operator!=(const COLOR4D &lhs, const COLOR4D &rhs)
Not equality operator, are two colors not equal.
Definition: color4d.cpp:254
Number of colors.
Definition: colors.h:75
#define min(a, b)
Definition: auxiliary.h:85
EDA_COLOR_T NextColor(EDA_COLOR_T &aColor)
Definition: colors.h:87
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39