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;
194 
195  if( hues[candidate] == 0.0 && values[candidate] == 0.0 )
196  {
197  COLOR4D candidate4d( candidate );
198  double cs, cv;
199 
200  candidate4d.ToHSV( ch, cs, cv );
201 
202  values[candidate] = cv;
203  // Set the hue to non-zero for black so that we won't do this more than once
204  hues[candidate] = ( cv == 0.0 ) ? 1.0 : ch;
205  }
206  else
207  {
208  ch = hues[candidate];
209  }
210 
211  if( fabs( ch - h ) < minDist )
212  {
213  minDist = fabs( ch - h );
214  legacyHue = ch;
215  }
216  }
217  }
218 
219  // Now we have the desired hue; let's find the nearest value
220  minDist = 1.0;
221  for( EDA_COLOR_T candidate = ::BLACK;
222  candidate < NBCOLORS; candidate = NextColor( candidate ) )
223  {
224  // If the target hue is NAN, we didn't extract the value for any colors above
225  if( std::isnan( legacyHue ) )
226  {
227  double ch, cs, cv;
228  COLOR4D candidate4d( candidate );
229  candidate4d.ToHSV( ch, cs, cv );
230  values[candidate] = cv;
231  hues[candidate] = ( cv == 0.0 ) ? 1.0 : ch;
232  }
233 
234  if( ( std::isnan( legacyHue ) != std::isnan( hues[candidate] ) ) || hues[candidate] != legacyHue )
235  continue;
236 
237  if( fabs( values[candidate] - v ) < minDist )
238  {
239  minDist = fabs( values[candidate] - v );
240  nearest = candidate;
241  }
242  }
243 
244  nearestCache.insert( std::pair< unsigned int, unsigned int >(
245  colorInt, static_cast<unsigned int>( nearest ) ) );
246 
247  return nearest;
248  }
249 #endif
250 
251 namespace KIGFX {
252 
253 const bool operator==( const COLOR4D& lhs, const COLOR4D& rhs )
254 {
255  return lhs.a == rhs.a && lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b;
256 }
257 
258 
259 const bool operator!=( const COLOR4D& lhs, const COLOR4D& rhs )
260 {
261  return !( lhs == rhs );
262 }
263 
264 std::ostream &operator<<( std::ostream &aStream, COLOR4D const &aColor )
265 {
266  return aStream << aColor.ToWxString( wxC2S_CSS_SYNTAX );
267 }
268 
269 }
270 
271 
272 void COLOR4D::ToHSV( double& aOutHue, double& aOutSaturation, double& aOutValue, bool aAlwaysDefineHue ) const
273 {
274  double min, max, delta;
275 
276  min = r < g ? r : g;
277  min = min < b ? min : b;
278 
279  max = r > g ? r : g;
280  max = max > b ? max : b;
281 
282  aOutValue = max; // value
283  delta = max - min;
284 
285  if( max > 0.0 )
286  {
287  aOutSaturation = ( delta / max );
288  }
289  else // for black color (r = g = b = 0 ) saturation is set to 0.
290  {
291  aOutSaturation = 0.0;
292  aOutHue = aAlwaysDefineHue ? 0.0 : NAN;
293  return;
294  }
295 
296  /* Hue in degrees (0...360) is coded according to this table
297  * 0 or 360 : red
298  * 60 : yellow
299  * 120 : green
300  * 180 : cyan
301  * 240 : blue
302  * 300 : magenta
303  */
304  if( delta != 0.0 )
305  {
306  if( r >= max )
307  aOutHue = ( g - b ) / delta; // between yellow & magenta
308  else if( g >= max )
309  aOutHue = 2.0 + ( b - r ) / delta; // between cyan & yellow
310  else
311  aOutHue = 4.0 + ( r - g ) / delta; // between magenta & cyan
312 
313  aOutHue *= 60.0; // degrees
314 
315  if( aOutHue < 0.0 )
316  aOutHue += 360.0;
317  }
318  else // delta = 0 means r = g = b. hue is set to 0.0
319  {
320  aOutHue = aAlwaysDefineHue ? 0.0 : NAN;
321  }
322 }
323 
324 
325 void COLOR4D::FromHSV( double aInH, double aInS, double aInV )
326 {
327  if( aInS <= 0.0 )
328  {
329  r = aInV;
330  g = aInV;
331  b = aInV;
332  return;
333  }
334 
335  double hh = aInH;
336 
337  while( hh >= 360.0 )
338  hh -= 360.0;
339 
340  /* Hue in degrees (0...360) is coded according to this table
341  * 0 or 360 : red
342  * 60 : yellow
343  * 120 : green
344  * 180 : cyan
345  * 240 : blue
346  * 300 : magenta
347  */
348  hh /= 60.0;
349 
350  int i = (int) hh;
351  double ff = hh - i;
352 
353  double p = aInV * ( 1.0 - aInS );
354  double q = aInV * ( 1.0 - ( aInS * ff ) );
355  double t = aInV * ( 1.0 - ( aInS * ( 1.0 - ff ) ) );
356 
357  switch( i )
358  {
359  case 0:
360  r = aInV;
361  g = t;
362  b = p;
363  break;
364 
365  case 1:
366  r = q;
367  g = aInV;
368  b = p;
369  break;
370 
371  case 2:
372  r = p;
373  g = aInV;
374  b = t;
375  break;
376 
377  case 3:
378  r = p;
379  g = q;
380  b = aInV;
381  break;
382 
383  case 4:
384  r = t;
385  g = p;
386  b = aInV;
387  break;
388 
389  case 5:
390  default:
391  r = aInV;
392  g = p;
393  b = q;
394  break;
395  }
396 }
397 
398 
399 COLOR4D& COLOR4D::Saturate( double aFactor )
400 {
401  double h, s, v;
402 
403  ToHSV( h, s, v );
404  FromHSV( h, aFactor, 1.0 );
405 
406  return *this;
407 }
408 
409 const COLOR4D COLOR4D::UNSPECIFIED( 0, 0, 0, 0 );
410 const COLOR4D COLOR4D::WHITE( 1, 1, 1, 1 );
411 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:58
COLOR4D & Saturate(double aFactor)
Saturates the color to a given factor (in HSV model)
Definition: color4d.cpp:399
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&#39;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:272
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:264
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:325
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:253
size_t i
Definition: json11.cpp:597
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:259
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