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  wxColour COLOR4D::ToColour() const
83  {
84  using CHAN_T = wxColourBase::ChannelType;
85 
86  const wxColour colour(
87  static_cast<CHAN_T>( r * 255 + 0.5 ),
88  static_cast<CHAN_T>( g * 255 + 0.5 ),
89  static_cast<CHAN_T>( b * 255 + 0.5 ),
90  static_cast<CHAN_T>( a * 255 + 0.5 )
91  );
92  return colour;
93  }
94 
95 
96  COLOR4D COLOR4D::LegacyMix( COLOR4D aColor ) const
97  {
98  COLOR4D candidate;
99 
100  // Blend the two colors (i.e. OR the RGB values)
101  candidate.r = ( (unsigned)( 255.0 * r ) | (unsigned)( 255.0 * aColor.r ) ) / 255.0,
102  candidate.g = ( (unsigned)( 255.0 * g ) | (unsigned)( 255.0 * aColor.g ) ) / 255.0,
103  candidate.b = ( (unsigned)( 255.0 * b ) | (unsigned)( 255.0 * aColor.b ) ) / 255.0,
104 
105  // the alpha channel can be reinitialized
106  // but what is the best value?
107  candidate.a = ( aColor.a + a ) / 2;
108 
109  return candidate;
110  }
111 
112 
113  COLOR4D& COLOR4D::SetToLegacyHighlightColor()
114  {
115  EDA_COLOR_T legacyColor = GetNearestLegacyColor( *this );
116  EDA_COLOR_T highlightColor = g_ColorRefs[legacyColor].m_LightColor;
117 
118  // The alpha channel is not modified. Only R, G, B values are set,
119  // because legacy color does not know the alpha chanel.
120 
121  r = g_ColorRefs[highlightColor].m_Red / 255.0;
122  g = g_ColorRefs[highlightColor].m_Green / 255.0;
123  b = g_ColorRefs[highlightColor].m_Blue / 255.0;
124 
125  return *this;
126  }
127 
128 
129  COLOR4D& COLOR4D::SetToNearestLegacyColor()
130  {
131  EDA_COLOR_T legacyColor = GetNearestLegacyColor( *this );
132 
133  // The alpha channel is not modified. Only R, G, B values are set,
134  // because legacy color does not know the alpha chanel.
135 
136  r = g_ColorRefs[legacyColor].m_Red / 255.0;
137  g = g_ColorRefs[legacyColor].m_Green / 255.0;
138  b = g_ColorRefs[legacyColor].m_Blue / 255.0;
139 
140  return *this;
141  }
142 
143 
144  unsigned int COLOR4D::ToU32() const
145  {
146  return ToColour().GetRGB();
147  }
148 
149 
150  void COLOR4D::FromU32( unsigned int aPackedColor )
151  {
152  wxColour c;
153  c.SetRGB( aPackedColor );
154  r = c.Red() / 255.0;
155  g = c.Green() / 255.0;
156  b = c.Blue() / 255.0;
157  a = c.Alpha() / 255.0;
158  }
159 
160 
161  EDA_COLOR_T COLOR4D::GetNearestLegacyColor( const COLOR4D &aColor )
162  {
163  // Cache layer implemented here, because all callers are using wxColour
164  static std::map< unsigned int, unsigned int > nearestCache;
165  static double hues[NBCOLORS];
166  static double values[NBCOLORS];
167 
168  unsigned int colorInt = aColor.ToU32();
169 
170  auto search = nearestCache.find( colorInt );
171 
172  if( search != nearestCache.end() )
173  return static_cast<EDA_COLOR_T>( search->second );
174 
175  // First use ColorFindNearest to check for exact matches
176  EDA_COLOR_T nearest = ColorFindNearest( aColor.r * 255.0, aColor.g * 255.0, aColor.b * 255.0 );
177 
178  if( COLOR4D( nearest ) == aColor )
179  {
180  nearestCache.insert( std::pair< unsigned int, unsigned int >(
181  colorInt, static_cast<unsigned int>( nearest ) ) );
182  return nearest;
183  }
184 
185  // If not, use hue and value to match.
186  // Hue will be NAN for grayscale colors.
187  // The legacy color palette is a grid across hue and value.
188  // We can exploit that to find a good match -- hue is most apparent to the user.
189  // So, first we determine the closest hue match, and then the closest value from that
190  // "grid row" in the legacy palette.
191 
192  double h, s, v;
193  aColor.ToHSV( h, s, v );
194 
195  double minDist = 360.0;
196  double legacyHue = 0.0;
197 
198  if( std::isnan( h ) )
199  {
200  legacyHue = NAN;
201  }
202  else
203  {
204  for( EDA_COLOR_T candidate = ::BLACK;
205  candidate < NBCOLORS; candidate = NextColor( candidate ) )
206  {
207  double ch;
208 
209  if( hues[candidate] == 0.0 && values[candidate] == 0.0 )
210  {
211  COLOR4D candidate4d( candidate );
212  double cs, cv;
213 
214  candidate4d.ToHSV( ch, cs, cv );
215 
216  values[candidate] = cv;
217  // Set the hue to non-zero for black so that we won't do this more than once
218  hues[candidate] = ( cv == 0.0 ) ? 1.0 : ch;
219  }
220  else
221  {
222  ch = hues[candidate];
223  }
224 
225  if( fabs( ch - h ) < minDist )
226  {
227  minDist = fabs( ch - h );
228  legacyHue = ch;
229  }
230  }
231  }
232 
233  // Now we have the desired hue; let's find the nearest value
234  minDist = 1.0;
235  for( EDA_COLOR_T candidate = ::BLACK;
236  candidate < NBCOLORS; candidate = NextColor( candidate ) )
237  {
238  // If the target hue is NAN, we didn't extract the value for any colors above
239  if( std::isnan( legacyHue ) )
240  {
241  double ch, cs, cv;
242  COLOR4D candidate4d( candidate );
243  candidate4d.ToHSV( ch, cs, cv );
244  values[candidate] = cv;
245  hues[candidate] = ( cv == 0.0 ) ? 1.0 : ch;
246  }
247 
248  if( ( std::isnan( legacyHue ) != std::isnan( hues[candidate] ) ) || hues[candidate] != legacyHue )
249  continue;
250 
251  if( fabs( values[candidate] - v ) < minDist )
252  {
253  minDist = fabs( values[candidate] - v );
254  nearest = candidate;
255  }
256  }
257 
258  nearestCache.insert( std::pair< unsigned int, unsigned int >(
259  colorInt, static_cast<unsigned int>( nearest ) ) );
260 
261  return nearest;
262  }
263 #endif
264 
265 namespace KIGFX {
266 
267 const bool operator==( const COLOR4D& lhs, const COLOR4D& rhs )
268 {
269  return lhs.a == rhs.a && lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b;
270 }
271 
272 
273 const bool operator!=( const COLOR4D& lhs, const COLOR4D& rhs )
274 {
275  return !( lhs == rhs );
276 }
277 
278 std::ostream &operator<<( std::ostream &aStream, COLOR4D const &aColor )
279 {
280  return aStream << aColor.ToWxString( wxC2S_CSS_SYNTAX );
281 }
282 
283 }
284 
285 
286 void COLOR4D::ToHSV( double& aOutHue, double& aOutSaturation, double& aOutValue, bool aAlwaysDefineHue ) const
287 {
288  double min, max, delta;
289 
290  min = r < g ? r : g;
291  min = min < b ? min : b;
292 
293  max = r > g ? r : g;
294  max = max > b ? max : b;
295 
296  aOutValue = max; // value
297  delta = max - min;
298 
299  if( max > 0.0 )
300  {
301  aOutSaturation = ( delta / max );
302  }
303  else // for black color (r = g = b = 0 ) saturation is set to 0.
304  {
305  aOutSaturation = 0.0;
306  aOutHue = aAlwaysDefineHue ? 0.0 : NAN;
307  return;
308  }
309 
310  /* Hue in degrees (0...360) is coded according to this table
311  * 0 or 360 : red
312  * 60 : yellow
313  * 120 : green
314  * 180 : cyan
315  * 240 : blue
316  * 300 : magenta
317  */
318  if( delta != 0.0 )
319  {
320  if( r >= max )
321  aOutHue = ( g - b ) / delta; // between yellow & magenta
322  else if( g >= max )
323  aOutHue = 2.0 + ( b - r ) / delta; // between cyan & yellow
324  else
325  aOutHue = 4.0 + ( r - g ) / delta; // between magenta & cyan
326 
327  aOutHue *= 60.0; // degrees
328 
329  if( aOutHue < 0.0 )
330  aOutHue += 360.0;
331  }
332  else // delta = 0 means r = g = b. hue is set to 0.0
333  {
334  aOutHue = aAlwaysDefineHue ? 0.0 : NAN;
335  }
336 }
337 
338 
339 void COLOR4D::FromHSV( double aInH, double aInS, double aInV )
340 {
341  if( aInS <= 0.0 )
342  {
343  r = aInV;
344  g = aInV;
345  b = aInV;
346  return;
347  }
348 
349  double hh = aInH;
350 
351  while( hh >= 360.0 )
352  hh -= 360.0;
353 
354  /* Hue in degrees (0...360) is coded according to this table
355  * 0 or 360 : red
356  * 60 : yellow
357  * 120 : green
358  * 180 : cyan
359  * 240 : blue
360  * 300 : magenta
361  */
362  hh /= 60.0;
363 
364  int i = (int) hh;
365  double ff = hh - i;
366 
367  double p = aInV * ( 1.0 - aInS );
368  double q = aInV * ( 1.0 - ( aInS * ff ) );
369  double t = aInV * ( 1.0 - ( aInS * ( 1.0 - ff ) ) );
370 
371  switch( i )
372  {
373  case 0:
374  r = aInV;
375  g = t;
376  b = p;
377  break;
378 
379  case 1:
380  r = q;
381  g = aInV;
382  b = p;
383  break;
384 
385  case 2:
386  r = p;
387  g = aInV;
388  b = t;
389  break;
390 
391  case 3:
392  r = p;
393  g = q;
394  b = aInV;
395  break;
396 
397  case 4:
398  r = t;
399  g = p;
400  b = aInV;
401  break;
402 
403  case 5:
404  default:
405  r = aInV;
406  g = p;
407  b = q;
408  break;
409  }
410 }
411 
412 
413 COLOR4D& COLOR4D::Saturate( double aFactor )
414 {
415  // One can saturate a color only when r, v, b are not equal
416  if( r == g && r == b )
417  return *this;
418 
419  double h, s, v;
420 
421  ToHSV( h, s, v, true );
422  FromHSV( h, aFactor, 1.0 );
423 
424  return *this;
425 }
426 
427 const COLOR4D COLOR4D::UNSPECIFIED( 0, 0, 0, 0 );
428 const COLOR4D COLOR4D::WHITE( 1, 1, 1, 1 );
429 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:413
unsigned char m_Green
Definition: colors.h:145
static const COLOR4D BLACK
Definition: color4d.h:297
double g
Green component.
Definition: color4d.h:288
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn&#39;t been set yet.
Definition: color4d.h:293
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:286
double b
Blue component.
Definition: color4d.h:289
double a
Alpha component.
Definition: color4d.h:290
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:296
std::ostream & operator<<(std::ostream &aStream, COLOR4D const &aColor)
Syntactic sugar for outputting colors to strings.
Definition: color4d.cpp:278
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:339
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:267
size_t i
Definition: json11.cpp:597
double r
Red component.
Definition: color4d.h:287
const bool operator!=(const COLOR4D &lhs, const COLOR4D &rhs)
Not equality operator, are two colors not equal.
Definition: color4d.cpp:273
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