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