You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

566 lines
15 KiB

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