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.

412 lines
11 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 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 <gal/color4d.h>
  27. using namespace KIGFX;
  28. COLOR4D::COLOR4D( EDA_COLOR_T aColor )
  29. {
  30. if( aColor <= UNSPECIFIED_COLOR || aColor >= NBCOLORS )
  31. {
  32. *this = COLOR4D::UNSPECIFIED;
  33. return;
  34. }
  35. r = g_ColorRefs[aColor].m_Red / 255.0;
  36. g = g_ColorRefs[aColor].m_Green / 255.0;
  37. b = g_ColorRefs[aColor].m_Blue / 255.0;
  38. a = 1.0;
  39. }
  40. #ifdef WX_COMPATIBILITY
  41. COLOR4D::COLOR4D( const wxColour& aColor )
  42. {
  43. r = aColor.Red() / 255.0;
  44. g = aColor.Green() / 255.0;
  45. b = aColor.Blue() / 255.0;
  46. a = aColor.Alpha() / 255.0;
  47. }
  48. bool COLOR4D::SetFromWxString( const wxString& aColorString )
  49. {
  50. wxColour c;
  51. if( c.Set( aColorString ) )
  52. {
  53. r = c.Red() / 255.0;
  54. g = c.Green() / 255.0;
  55. b = c.Blue() / 255.0;
  56. a = c.Alpha() / 255.0;
  57. return true;
  58. }
  59. return false;
  60. }
  61. wxString COLOR4D::ToWxString( long flags ) const
  62. {
  63. wxColour c = ToColour();
  64. return c.GetAsString( flags );
  65. }
  66. COLOR4D COLOR4D::LegacyMix( COLOR4D aColor ) const
  67. {
  68. COLOR4D candidate;
  69. // Blend the two colors (i.e. OR the RGB values)
  70. candidate.r = ( (unsigned)( 255.0 * r ) | (unsigned)( 255.0 * aColor.r ) ) / 255.0,
  71. candidate.g = ( (unsigned)( 255.0 * g ) | (unsigned)( 255.0 * aColor.g ) ) / 255.0,
  72. candidate.b = ( (unsigned)( 255.0 * b ) | (unsigned)( 255.0 * aColor.b ) ) / 255.0,
  73. // the alpha channel can be reinitialized
  74. // but what is the best value?
  75. candidate.a = ( aColor.a + a ) / 2;
  76. return candidate;
  77. }
  78. COLOR4D& COLOR4D::SetToLegacyHighlightColor()
  79. {
  80. EDA_COLOR_T legacyColor = GetNearestLegacyColor( *this );
  81. EDA_COLOR_T highlightColor = g_ColorRefs[legacyColor].m_LightColor;
  82. // The alpha channel is not modified. Only R, G, B values are set,
  83. // because legacy color does not know the alpha chanel.
  84. r = g_ColorRefs[highlightColor].m_Red / 255.0;
  85. g = g_ColorRefs[highlightColor].m_Green / 255.0;
  86. b = g_ColorRefs[highlightColor].m_Blue / 255.0;
  87. return *this;
  88. }
  89. COLOR4D& COLOR4D::SetToNearestLegacyColor()
  90. {
  91. EDA_COLOR_T legacyColor = GetNearestLegacyColor( *this );
  92. // The alpha channel is not modified. Only R, G, B values are set,
  93. // because legacy color does not know the alpha chanel.
  94. r = g_ColorRefs[legacyColor].m_Red / 255.0;
  95. g = g_ColorRefs[legacyColor].m_Green / 255.0;
  96. b = g_ColorRefs[legacyColor].m_Blue / 255.0;
  97. return *this;
  98. }
  99. unsigned int COLOR4D::ToU32() const
  100. {
  101. return ToColour().GetRGB();
  102. }
  103. void COLOR4D::FromU32( unsigned int aPackedColor )
  104. {
  105. wxColour c;
  106. c.SetRGB( aPackedColor );
  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. EDA_COLOR_T COLOR4D::GetNearestLegacyColor( COLOR4D &aColor )
  113. {
  114. // Cache layer implemented here, because all callers are using wxColour
  115. static std::map< unsigned int, unsigned int > nearestCache;
  116. static double hues[NBCOLORS];
  117. static double values[NBCOLORS];
  118. unsigned int colorInt = aColor.ToU32();
  119. auto search = nearestCache.find( colorInt );
  120. if( search != nearestCache.end() )
  121. return static_cast<EDA_COLOR_T>( search->second );
  122. // First use ColorFindNearest to check for exact matches
  123. EDA_COLOR_T nearest = ColorFindNearest( aColor.r * 255.0, aColor.g * 255.0, aColor.b * 255.0 );
  124. if( COLOR4D( nearest ) == aColor )
  125. {
  126. nearestCache.insert( std::pair< unsigned int, unsigned int >(
  127. colorInt, static_cast<unsigned int>( nearest ) ) );
  128. return nearest;
  129. }
  130. // If not, use hue and value to match.
  131. // Hue will be NAN for grayscale colors.
  132. // The legacy color palette is a grid across hue and value.
  133. // We can exploit that to find a good match -- hue is most apparent to the user.
  134. // So, first we determine the closest hue match, and then the closest value from that
  135. // "grid row" in the legacy palette.
  136. double h, s, v;
  137. aColor.ToHSV( h, s, v );
  138. double minDist = 360.0;
  139. double legacyHue = 0.0;
  140. if( std::isnan( h ) )
  141. {
  142. legacyHue = NAN;
  143. }
  144. else
  145. {
  146. for( EDA_COLOR_T candidate = ::BLACK;
  147. candidate < NBCOLORS; candidate = NextColor( candidate ) )
  148. {
  149. double ch, cs, cv;
  150. if( hues[candidate] == 0.0 && values[candidate] == 0.0 )
  151. {
  152. COLOR4D candidate4d( candidate );
  153. candidate4d.ToHSV( ch, cs, cv );
  154. values[candidate] = cv;
  155. // Set the hue to non-zero for black so that we won't do this more than once
  156. hues[candidate] = ( cv == 0.0 ) ? 1.0 : ch;
  157. }
  158. else
  159. {
  160. ch = hues[candidate];
  161. cv = values[candidate];
  162. cv = 0.0;
  163. }
  164. if( fabs( ch - h ) < minDist )
  165. {
  166. minDist = fabs( ch - h );
  167. legacyHue = ch;
  168. }
  169. }
  170. }
  171. // Now we have the desired hue; let's find the nearest value
  172. minDist = 1.0;
  173. for( EDA_COLOR_T candidate = ::BLACK;
  174. candidate < NBCOLORS; candidate = NextColor( candidate ) )
  175. {
  176. // If the target hue is NAN, we didn't extract the value for any colors above
  177. if( std::isnan( legacyHue ) )
  178. {
  179. double ch, cs, cv;
  180. COLOR4D candidate4d( candidate );
  181. candidate4d.ToHSV( ch, cs, cv );
  182. values[candidate] = cv;
  183. hues[candidate] = ( cv == 0.0 ) ? 1.0 : ch;
  184. }
  185. if( ( std::isnan( legacyHue ) != std::isnan( hues[candidate] ) ) || hues[candidate] != legacyHue )
  186. continue;
  187. if( fabs( values[candidate] - v ) < minDist )
  188. {
  189. minDist = fabs( values[candidate] - v );
  190. nearest = candidate;
  191. }
  192. }
  193. nearestCache.insert( std::pair< unsigned int, unsigned int >(
  194. colorInt, static_cast<unsigned int>( nearest ) ) );
  195. return nearest;
  196. }
  197. #endif
  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. std::ostream &operator<<( std::ostream &aStream, COLOR4D const &aColor )
  208. {
  209. return aStream << aColor.ToWxString( wxC2S_CSS_SYNTAX );
  210. }
  211. }
  212. void COLOR4D::ToHSV( double& aOutHue, double& aOutSaturation, double& aOutValue, bool aAlwaysDefineHue ) const
  213. {
  214. double min, max, delta;
  215. min = r < g ? r : g;
  216. min = min < b ? min : b;
  217. max = r > g ? r : g;
  218. max = max > b ? max : b;
  219. aOutValue = max; // value
  220. delta = max - min;
  221. if( max > 0.0 )
  222. {
  223. aOutSaturation = ( delta / max );
  224. }
  225. else // for black color (r = g = b = 0 ) saturation is set to 0.
  226. {
  227. aOutSaturation = 0.0;
  228. aOutHue = aAlwaysDefineHue ? 0.0 : NAN;
  229. return;
  230. }
  231. /* Hue in degrees (0...360) is coded according to this table
  232. * 0 or 360 : red
  233. * 60 : yellow
  234. * 120 : green
  235. * 180 : cyan
  236. * 240 : blue
  237. * 300 : magenta
  238. */
  239. if( delta != 0.0 )
  240. {
  241. if( r >= max )
  242. aOutHue = ( g - b ) / delta; // between yellow & magenta
  243. else if( g >= max )
  244. aOutHue = 2.0 + ( b - r ) / delta; // between cyan & yellow
  245. else
  246. aOutHue = 4.0 + ( r - g ) / delta; // between magenta & cyan
  247. aOutHue *= 60.0; // degrees
  248. if( aOutHue < 0.0 )
  249. aOutHue += 360.0;
  250. }
  251. else // delta = 0 means r = g = b. hue is set to 0.0
  252. {
  253. aOutHue = aAlwaysDefineHue ? 0.0 : NAN;
  254. }
  255. }
  256. void COLOR4D::FromHSV( double aInH, double aInS, double aInV )
  257. {
  258. if( aInS <= 0.0 )
  259. {
  260. r = aInV;
  261. g = aInV;
  262. b = aInV;
  263. return;
  264. }
  265. double hh = aInH;
  266. while( hh >= 360.0 )
  267. hh -= 360.0;
  268. /* Hue in degrees (0...360) is coded according to this table
  269. * 0 or 360 : red
  270. * 60 : yellow
  271. * 120 : green
  272. * 180 : cyan
  273. * 240 : blue
  274. * 300 : magenta
  275. */
  276. hh /= 60.0;
  277. int i = (int) hh;
  278. double ff = hh - i;
  279. double p = aInV * ( 1.0 - aInS );
  280. double q = aInV * ( 1.0 - ( aInS * ff ) );
  281. double t = aInV * ( 1.0 - ( aInS * ( 1.0 - ff ) ) );
  282. switch( i )
  283. {
  284. case 0:
  285. r = aInV;
  286. g = t;
  287. b = p;
  288. break;
  289. case 1:
  290. r = q;
  291. g = aInV;
  292. b = p;
  293. break;
  294. case 2:
  295. r = p;
  296. g = aInV;
  297. b = t;
  298. break;
  299. case 3:
  300. r = p;
  301. g = q;
  302. b = aInV;
  303. break;
  304. case 4:
  305. r = t;
  306. g = p;
  307. b = aInV;
  308. break;
  309. case 5:
  310. default:
  311. r = aInV;
  312. g = p;
  313. b = q;
  314. break;
  315. }
  316. }
  317. COLOR4D& COLOR4D::Saturate( double aFactor )
  318. {
  319. double h, s, v;
  320. ToHSV( h, s, v );
  321. FromHSV( h, aFactor, 1.0 );
  322. return *this;
  323. }
  324. const COLOR4D COLOR4D::UNSPECIFIED( 0, 0, 0, 0 );
  325. const COLOR4D COLOR4D::WHITE( 1, 1, 1, 1 );
  326. const COLOR4D COLOR4D::BLACK( 0, 0, 0, 1 );