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-2019 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 #include <nlohmann/json.hpp>
28 #include <gal/color4d.h>
29 #include <i18n_utility.h>
30 
31 using namespace KIGFX;
32 
33 #define TS( string ) wxString( _HKI( string ) ).ToStdString()
34 
35 // We can't have this as a plain static variable, because it is referenced during the initialization
36 // of other static variables, so we must initialize it explicitly on first use.
38 {
39  static StructColors s_ColorRefs[NBCOLORS] =
40  {
41  { 0, 0, 0, BLACK, TS( "Black" ), DARKDARKGRAY },
42  { 72, 72, 72, DARKDARKGRAY, TS( "Gray 1" ), DARKGRAY },
43  { 132, 132, 132, DARKGRAY, TS( "Gray 2" ), LIGHTGRAY },
44  { 194, 194, 194, LIGHTGRAY, TS( "Gray 3" ), WHITE },
45  { 255, 255, 255, WHITE, TS( "White" ), WHITE },
46  { 194, 255, 255, LIGHTYELLOW, TS( "L.Yellow" ), WHITE },
47  { 191, 229, 255, LIGHTERORANGE, TS( "L.Orange" ), WHITE },
48  { 72, 0, 0, DARKBLUE, TS( "Blue 1" ), BLUE },
49  { 0, 72, 0, DARKGREEN, TS( "Green 1" ), GREEN },
50  { 72, 72, 0, DARKCYAN, TS( "Cyan 1" ), CYAN },
51  { 0, 0, 72, DARKRED, TS( "Red 1" ), RED },
52  { 72, 0, 72, DARKMAGENTA, TS( "Magenta 1" ), MAGENTA },
53  { 0, 72, 72, DARKBROWN, TS( "Brown 1" ), BROWN },
54  { 0, 77, 128, DARKORANGE, TS( "Orange 1" ), ORANGE },
55  { 132, 0, 0, BLUE, TS( "Blue 2" ), LIGHTBLUE },
56  { 0, 132, 0, GREEN, TS( "Green 2" ), LIGHTGREEN },
57  { 132, 132, 0, CYAN, TS( "Cyan 2" ), LIGHTCYAN },
58  { 0, 0, 132, RED, TS( "Red 2" ), LIGHTRED },
59  { 132, 0, 132, MAGENTA, TS( "Magenta 2" ), LIGHTMAGENTA },
60  { 0, 132, 132, BROWN, TS( "Brown 2" ), YELLOW },
61  { 0, 102, 204, ORANGE, TS( "Orange 2" ), LIGHTORANGE },
62  { 194, 0, 0, LIGHTBLUE, TS( "Blue 3" ), PUREBLUE, },
63  { 0, 194, 0, LIGHTGREEN, TS( "Green 3" ), PUREGREEN },
64  { 194, 194, 0, LIGHTCYAN, TS( "Cyan 3" ), PURECYAN },
65  { 0, 0, 194, LIGHTRED, TS( "Red 3" ), PURERED },
66  { 194, 0, 194, LIGHTMAGENTA, TS( "Magenta 3" ), PUREMAGENTA },
67  { 0, 194, 194, YELLOW, TS( "Yellow 3" ), PUREYELLOW },
68  { 0, 133, 221, LIGHTORANGE, TS( "Orange 3" ), PUREORANGE },
69  { 255, 0, 0, PUREBLUE, TS( "Blue 4" ), WHITE },
70  { 0, 255, 0, PUREGREEN, TS( "Green 4" ), WHITE },
71  { 255, 255, 0, PURECYAN, TS( "Cyan 4" ), WHITE },
72  { 0, 0, 255, PURERED, TS( "Red 4" ), WHITE },
73  { 255, 0, 255, PUREMAGENTA, TS( "Magenta 4" ), WHITE },
74  { 0, 255, 255, PUREYELLOW, TS( "Yellow 4" ), WHITE },
75  { 0, 153, 255, PUREORANGE, TS( "Orange 4" ), WHITE },
76  };
77  return s_ColorRefs;
78 }
79 
80 
82 {
83  if( aColor <= UNSPECIFIED_COLOR || aColor >= NBCOLORS )
84  {
85  *this = COLOR4D::UNSPECIFIED;
86  return;
87  }
88 
89  int candidate = 0;
90 
91  for( ; candidate < NBCOLORS; ++candidate )
92  {
93  if( colorRefs()[candidate].m_Numcolor == aColor )
94  break;
95  }
96 
97  if( candidate >= NBCOLORS )
98  {
99  *this = COLOR4D::UNSPECIFIED;
100  return;
101  }
102 
103  r = colorRefs()[candidate].m_Red / 255.0;
104  g = colorRefs()[candidate].m_Green / 255.0;
105  b = colorRefs()[candidate].m_Blue / 255.0;
106  a = 1.0;
107 }
108 
109 
110 #ifdef WX_COMPATIBILITY
111  COLOR4D::COLOR4D( const wxColour& aColor )
112  {
113  r = aColor.Red() / 255.0;
114  g = aColor.Green() / 255.0;
115  b = aColor.Blue() / 255.0;
116  a = aColor.Alpha() / 255.0;
117  }
118 
119 
120  bool COLOR4D::SetFromWxString( const wxString& aColorString )
121  {
122  wxColour c;
123 
124  if( c.Set( aColorString ) )
125  {
126  r = c.Red() / 255.0;
127  g = c.Green() / 255.0;
128  b = c.Blue() / 255.0;
129  a = c.Alpha() / 255.0;
130 
131  return true;
132  }
133 
134  return false;
135  }
136 
137 
138  wxString COLOR4D::ToWxString( long flags ) const
139  {
140  wxColour c = ToColour();
141  return c.GetAsString( flags );
142  }
143 
144 
145  wxColour COLOR4D::ToColour() const
146  {
147  using CHAN_T = wxColourBase::ChannelType;
148 
149  const wxColour colour(
150  static_cast<CHAN_T>( r * 255 + 0.5 ),
151  static_cast<CHAN_T>( g * 255 + 0.5 ),
152  static_cast<CHAN_T>( b * 255 + 0.5 ),
153  static_cast<CHAN_T>( a * 255 + 0.5 )
154  );
155  return colour;
156  }
157 
158 
159  COLOR4D COLOR4D::LegacyMix( COLOR4D aColor ) const
160  {
161  COLOR4D candidate;
162 
163  // Blend the two colors (i.e. OR the RGB values)
164  candidate.r = ( (unsigned)( 255.0 * r ) | (unsigned)( 255.0 * aColor.r ) ) / 255.0,
165  candidate.g = ( (unsigned)( 255.0 * g ) | (unsigned)( 255.0 * aColor.g ) ) / 255.0,
166  candidate.b = ( (unsigned)( 255.0 * b ) | (unsigned)( 255.0 * aColor.b ) ) / 255.0,
167 
168  // the alpha channel can be reinitialized
169  // but what is the best value?
170  candidate.a = ( aColor.a + a ) / 2;
171 
172  return candidate;
173  }
174 
175 
176  unsigned int COLOR4D::ToU32() const
177  {
178  return ToColour().GetRGB();
179  }
180 
181 
182  void COLOR4D::FromU32( unsigned int aPackedColor )
183  {
184  wxColour c;
185  c.SetRGB( aPackedColor );
186  r = c.Red() / 255.0;
187  g = c.Green() / 255.0;
188  b = c.Blue() / 255.0;
189  a = c.Alpha() / 255.0;
190  }
191 
192 #endif
193 
194 namespace KIGFX {
195 
196 const bool operator==( const COLOR4D& lhs, const COLOR4D& rhs )
197 {
198  return lhs.a == rhs.a && lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b;
199 }
200 
201 
202 const bool operator!=( const COLOR4D& lhs, const COLOR4D& rhs )
203 {
204  return !( lhs == rhs );
205 }
206 
207 const bool operator<( const COLOR4D& lhs, const COLOR4D& rhs )
208 {
209  if( lhs.r < rhs.r )
210  return true;
211  else if( lhs.g < rhs.g )
212  return true;
213  else if( lhs.b < rhs.b )
214  return true;
215  else if( lhs.a < rhs.a )
216  return true;
217 
218  return false;
219 }
220 
221 std::ostream &operator<<( std::ostream &aStream, COLOR4D const &aColor )
222 {
223  return aStream << aColor.ToWxString( wxC2S_CSS_SYNTAX );
224 }
225 
226 void to_json( nlohmann::json& aJson, const COLOR4D& aColor )
227 {
228  aJson = nlohmann::json( aColor.ToWxString( wxC2S_CSS_SYNTAX ).ToStdString() );
229 }
230 
231 
232 void from_json( const nlohmann::json& aJson, COLOR4D& aColor )
233 {
234  aColor.SetFromWxString( aJson.get<std::string>() );
235 }
236 
237 }
238 
239 
240 void COLOR4D::ToHSL( double& aOutHue, double& aOutSaturation, double& aOutLightness ) const
241 {
242  auto min = std::min( r, std::min( g, b ) );
243  auto max = std::max( r, std::max( g, b ) );
244  auto diff = max - min;
245 
246  aOutLightness = ( max + min ) / 2.0;
247 
248  if( aOutLightness >= 1.0 )
249  aOutSaturation = 0.0;
250  else
251  aOutSaturation = diff / ( 1.0 - std::abs( 2.0 * aOutLightness - 1.0 ) );
252 
253  double hue;
254 
255  if( diff <= 0.0 )
256  hue = 0.0;
257  else if( max == r )
258  hue = ( g - b ) / diff;
259  else if( max == g )
260  hue = ( b - r ) / diff + 2.0;
261  else
262  hue = ( r - g ) / diff + 4.0;
263 
264  aOutHue = hue > 0.0 ? hue * 60.0 : hue * 60.0 + 360.0;
265 
266  while( aOutHue < 0.0 )
267  aOutHue += 360.0;
268 }
269 
270 
271 void COLOR4D::FromHSL( double aInHue, double aInSaturation, double aInLightness )
272 {
273  const auto P = ( 1.0 - std::abs( 2.0 * aInLightness - 1.0 ) ) * aInSaturation;
274  const auto scaled_hue = aInHue / 60.0;
275  const auto Q = P * ( 1.0 - std::abs( std::fmod( scaled_hue, 2.0 ) - 1.0 ) );
276 
277  r = g = b = aInLightness - P / 2.0;
278 
279  if (scaled_hue < 1.0)
280  {
281  r += P;
282  g += Q;
283  }
284  else if (scaled_hue < 2.0)
285  {
286  r += Q;
287  g += P;
288  }
289  else if (scaled_hue < 3.0)
290  {
291  g += P;
292  b += Q;
293  }
294  else if (scaled_hue < 4.0)
295  {
296  g += Q;
297  b += P;
298  }
299  else if (scaled_hue < 5.0)
300  {
301  r += Q;
302  b += P;
303  }
304  else
305  {
306  r += P;
307  b += Q;
308  }
309 }
310 
311 
312 void COLOR4D::ToHSV( double& aOutHue, double& aOutSaturation, double& aOutValue, bool aAlwaysDefineHue ) const
313 {
314  double min, max, delta;
315 
316  min = r < g ? r : g;
317  min = min < b ? min : b;
318 
319  max = r > g ? r : g;
320  max = max > b ? max : b;
321 
322  aOutValue = max; // value
323  delta = max - min;
324 
325  if( max > 0.0 )
326  {
327  aOutSaturation = ( delta / max );
328  }
329  else // for black color (r = g = b = 0 ) saturation is set to 0.
330  {
331  aOutSaturation = 0.0;
332  aOutHue = aAlwaysDefineHue ? 0.0 : NAN;
333  return;
334  }
335 
336  /* Hue in degrees (0...360) is coded according to this table
337  * 0 or 360 : red
338  * 60 : yellow
339  * 120 : green
340  * 180 : cyan
341  * 240 : blue
342  * 300 : magenta
343  */
344  if( delta != 0.0 )
345  {
346  if( r >= max )
347  aOutHue = ( g - b ) / delta; // between yellow & magenta
348  else if( g >= max )
349  aOutHue = 2.0 + ( b - r ) / delta; // between cyan & yellow
350  else
351  aOutHue = 4.0 + ( r - g ) / delta; // between magenta & cyan
352 
353  aOutHue *= 60.0; // degrees
354 
355  if( aOutHue < 0.0 )
356  aOutHue += 360.0;
357  }
358  else // delta = 0 means r = g = b. hue is set to 0.0
359  {
360  aOutHue = aAlwaysDefineHue ? 0.0 : NAN;
361  }
362 }
363 
364 
365 void COLOR4D::FromHSV( double aInH, double aInS, double aInV )
366 {
367  if( aInS <= 0.0 )
368  {
369  r = aInV;
370  g = aInV;
371  b = aInV;
372  return;
373  }
374 
375  double hh = aInH;
376 
377  while( hh >= 360.0 )
378  hh -= 360.0;
379 
380  /* Hue in degrees (0...360) is coded according to this table
381  * 0 or 360 : red
382  * 60 : yellow
383  * 120 : green
384  * 180 : cyan
385  * 240 : blue
386  * 300 : magenta
387  */
388  hh /= 60.0;
389 
390  int i = (int) hh;
391  double ff = hh - i;
392 
393  double p = aInV * ( 1.0 - aInS );
394  double q = aInV * ( 1.0 - ( aInS * ff ) );
395  double t = aInV * ( 1.0 - ( aInS * ( 1.0 - ff ) ) );
396 
397  switch( i )
398  {
399  case 0:
400  r = aInV;
401  g = t;
402  b = p;
403  break;
404 
405  case 1:
406  r = q;
407  g = aInV;
408  b = p;
409  break;
410 
411  case 2:
412  r = p;
413  g = aInV;
414  b = t;
415  break;
416 
417  case 3:
418  r = p;
419  g = q;
420  b = aInV;
421  break;
422 
423  case 4:
424  r = t;
425  g = p;
426  b = aInV;
427  break;
428 
429  case 5:
430  default:
431  r = aInV;
432  g = p;
433  b = q;
434  break;
435  }
436 }
437 
438 
439 COLOR4D& COLOR4D::Saturate( double aFactor )
440 {
441  // One can saturate a color only when r, v, b are not equal
442  if( r == g && r == b )
443  return *this;
444 
445  double h, s, v;
446 
447  ToHSV( h, s, v, true );
448  FromHSV( h, aFactor, 1.0 );
449 
450  return *this;
451 }
452 
453 const COLOR4D COLOR4D::UNSPECIFIED( 0, 0, 0, 0 );
454 const COLOR4D COLOR4D::WHITE( 1, 1, 1, 1 );
455 const COLOR4D COLOR4D::BLACK( 0, 0, 0, 1 );
456 const COLOR4D COLOR4D::CLEAR( 1, 0, 1, 0 );
457 
458 
460 {
461  EDA_COLOR_T candidate = EDA_COLOR_T::BLACK;
462 
463  /* Find the 'nearest' color in the palette. This is fun. There is
464  a gazilion of metrics for the color space and no one of the
465  useful one is in the RGB color space. Who cares, this is a CAD,
466  not a photosomething...
467 
468  I hereby declare that the distance is the sum of the square of the
469  component difference. Think about the RGB color cube. Now get the
470  euclidean distance, but without the square root... for ordering
471  purposes it's the same, obviously. Also each component can't be
472  less of the target one, since I found this currently work better...
473  */
474  int nearest_distance = 255 * 255 * 3 + 1; // Can't beat this
475 
476  for( EDA_COLOR_T trying = EDA_COLOR_T::BLACK; trying < EDA_COLOR_T::NBCOLORS;
477  trying = static_cast<EDA_COLOR_T>( int( trying ) + 1 ) )
478  {
479  const StructColors &c = colorRefs()[trying];
480  int distance = (aR - c.m_Red) * (aR - c.m_Red) +
481  (aG - c.m_Green) * (aG - c.m_Green) +
482  (aB - c.m_Blue) * (aB - c.m_Blue);
483 
484  if( distance < nearest_distance && c.m_Red >= aR &&
485  c.m_Green >= aG && c.m_Blue >= aB )
486  {
487  nearest_distance = distance;
488  candidate = trying;
489  }
490  }
491 
492  return candidate;
493 }
494 
495 COLOR4D& COLOR4D::FromCSSRGBA( int aRed, int aGreen, int aBlue, double aAlpha )
496 {
497  r = std::max( 0, std::min( 255, aRed ) ) / 255.0;
498  g = std::max( 0, std::min( 255, aGreen ) ) / 255.0;
499  b = std::max( 0, std::min( 255, aBlue ) ) / 255.0;
500  a = std::max( 0.0, std::min( 1.0, aAlpha ) );
501 
502  return *this;
503 }
void ToHSV(double &aOutHue, double &aOutSaturation, double &aOutValue, bool aAlwaysDefineHue=false) const
Convert current color (stored in RGB) to HSV format.
Definition: color4d.cpp:312
COLOR4D & FromCSSRGBA(int aRed, int aGreen, int aBlue, double aAlpha=1.0)
Initialize the color from a RGBA value with 0-255 red/green/blue and 0-1 alpha.
Definition: color4d.cpp:495
unsigned char m_Red
Definition: color4d.h:88
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:194
COLOR4D & Saturate(double aFactor)
Saturates the color to a given factor (in HSV model)
Definition: color4d.cpp:439
unsigned char m_Green
Definition: color4d.h:87
Definition: color4d.h:61
static const COLOR4D BLACK
Definition: color4d.h:373
double g
Green component.
Definition: color4d.h:364
Definition: color4d.h:44
void from_json(const nlohmann::json &aJson, COLOR4D &aColor)
Definition: color4d.cpp:232
nlohmann::json json
Definition: gerbview.cpp:41
const bool operator<(const COLOR4D &lhs, const COLOR4D &rhs)
Definition: color4d.cpp:207
double b
Blue component.
Definition: color4d.h:365
Number of colors.
Definition: color4d.h:79
Definition: color4d.h:76
Definition: color4d.h:67
double a
Alpha component.
Definition: color4d.h:366
static const COLOR4D CLEAR
Definition: color4d.h:374
Definition: color4d.h:57
unsigned char m_Blue
Definition: color4d.h:86
Definition: color4d.h:58
const StructColors * colorRefs()
Global list of legacy color names, still used all over the place for constructing COLOR4D's.
Definition: color4d.cpp:37
static const COLOR4D WHITE
Definition: color4d.h:372
void ToHSL(double &aOutHue, double &aOutSaturation, double &aOutValue) const
Converts current color (stored in RGB) to HSL format.
Definition: color4d.cpp:240
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
Definition: color4d.h:59
std::ostream & operator<<(std::ostream &aStream, COLOR4D const &aColor)
Syntactic sugar for outputting colors to strings.
Definition: color4d.cpp:221
void FromHSV(double aInH, double aInS, double aInV)
Changes currently used color to the one given by hue, saturation and value parameters.
Definition: color4d.cpp:365
EDA_COLOR_T
Legacy color enumeration.
Definition: color4d.h:41
void to_json(nlohmann::json &aJson, const COLOR4D &aColor)
Definition: color4d.cpp:226
Some functions to handle hotkeys in KiCad.
Definition: color4d.h:48
Definition: color4d.h:56
const bool operator==(const COLOR4D &lhs, const COLOR4D &rhs)
Equality operator, are two colors equal.
Definition: color4d.cpp:196
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition: color4d.h:369
static EDA_COLOR_T FindNearestLegacyColor(int aR, int aG, int aB)
Returns a legacy color ID that is closest to the given 8-bit RGB values.
Definition: color4d.cpp:459
double r
Red component.
Definition: color4d.h:363
const bool operator!=(const COLOR4D &lhs, const COLOR4D &rhs)
Not equality operator, are two colors not equal.
Definition: color4d.cpp:202
void FromHSL(double aInHue, double aInSaturation, double aInLightness)
Change currently used color to the one given by hue, saturation and lightness parameters.
Definition: color4d.cpp:271
#define TS(string)
Definition: color4d.cpp:33
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103