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