From 7b3ef1b7ed9ecf86a731b9655280f74a1cd37967 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Mon, 11 Feb 2019 19:55:03 -0800 Subject: [PATCH] colors: Add HSL conversion and tune colors When adding colors to length tuner, we depend on the window background color, which can be set by the user, resulting in an unreadable text. This uses the HSL conversion to detect which shade it should use for the coloring. Also adds test cases for round-trip HSL and HSV conversion Fixes: lp:1814530 * https://bugs.launchpad.net/kicad/+bug/1814530 --- common/gal/color4d.cpp | 74 ++++++++++++++++++++++++- include/gal/color4d.h | 22 ++++++++ pcbnew/router/pns_tune_status_popup.cpp | 25 +++++++-- qa/common/test_color4d.cpp | 58 ++++++++++++++++++- 4 files changed, 172 insertions(+), 7 deletions(-) diff --git a/common/gal/color4d.cpp b/common/gal/color4d.cpp index d4abcf5c01..823232bd50 100644 --- a/common/gal/color4d.cpp +++ b/common/gal/color4d.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright 2012 Torsten Hueter, torstenhtr gmx.de - * Copyright 2017 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright 2017-2019 Kicad Developers, see AUTHORS.txt for contributors. * * * This program is free software; you can redistribute it and/or @@ -283,6 +283,78 @@ std::ostream &operator<<( std::ostream &aStream, COLOR4D const &aColor ) } +void COLOR4D::ToHSL( double& aOutHue, double& aOutSaturation, double& aOutLightness ) const +{ + auto min = std::min( r, std::min( g, b ) ); + auto max = std::max( r, std::max( g, b ) ); + auto diff = max - min; + + aOutLightness = ( max + min ) / 2.0; + + if( aOutLightness >= 1.0 ) + aOutSaturation = 0.0; + else + aOutSaturation = diff / ( 1.0 - std::abs( 2.0 * aOutLightness - 1.0 ) ); + + double hue; + + if( diff <= 0.0 ) + hue = 0.0; + else if( max == r ) + hue = ( g - b ) / diff; + else if( max == g ) + hue = ( b - r ) / diff + 2.0; + else + hue = ( r - g ) / diff + 4.0; + + aOutHue = hue > 0.0 ? hue * 60.0 : hue * 60.0 + 360.0; + + while( aOutHue < 0.0 ) + aOutHue += 360.0; +} + + +void COLOR4D::FromHSL( double aInHue, double aInSaturation, double aInLightness ) +{ + const auto P = ( 1.0 - std::abs( 2.0 * aInLightness - 1.0 ) ) * aInSaturation; + const auto scaled_hue = aInHue / 60.0; + const auto Q = P * ( 1.0 - std::abs( std::fmod( scaled_hue, 2.0 ) - 1.0 ) ); + + r = g = b = aInLightness - P / 2.0; + + if (scaled_hue < 1.0) + { + r += P; + g += Q; + } + else if (scaled_hue < 2.0) + { + r += Q; + g += P; + } + else if (scaled_hue < 3.0) + { + g += P; + b += Q; + } + else if (scaled_hue < 4.0) + { + g += Q; + b += P; + } + else if (scaled_hue < 5.0) + { + r += Q; + b += P; + } + else + { + r += P; + b += Q; + } +} + + void COLOR4D::ToHSV( double& aOutHue, double& aOutSaturation, double& aOutValue, bool aAlwaysDefineHue ) const { double min, max, delta; diff --git a/include/gal/color4d.h b/include/gal/color4d.h index a5bf00c865..cdbf88d542 100644 --- a/include/gal/color4d.h +++ b/include/gal/color4d.h @@ -139,6 +139,28 @@ public: static EDA_COLOR_T GetNearestLegacyColor( const COLOR4D &aColor ); #endif /* WX_COMPATIBLITY */ + + /** + * Function ToHSL() + * Converts current color (stored in RGB) to HSL format. + * + * @param aOutHue is the conversion result for hue component, in degrees 0 ... 360.0 + * @param aOutSaturation is the conversion result for saturation component (0 ... 1.0). + * @param aOutLightness is conversion result for value component (0 ... 1.0). + * @note saturation is set to 0.0 for black color if r = g = b, + */ + void ToHSL( double& aOutHue, double& aOutSaturation, double& aOutValue ) const; + + /** + * Function FromHSL() + * Changes currently used color to the one given by hue, saturation and lightness parameters. + * + * @param aInHue is hue component, in degrees (0.0 - 360.0) + * @param aInSaturation is saturation component (0.0 - 1.0) + * @param aInLightness is lightness component (0.0 - 1.0) + */ + void FromHSL( double aInHue, double aInSaturation, double aInLightness ); + /** * Function Brighten * Makes the color brighter by a given factor. diff --git a/pcbnew/router/pns_tune_status_popup.cpp b/pcbnew/router/pns_tune_status_popup.cpp index 5c4c283d2c..be519a0c9c 100644 --- a/pcbnew/router/pns_tune_status_popup.cpp +++ b/pcbnew/router/pns_tune_status_popup.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014-2015 CERN - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2019 KiCad Developers, see AUTHORS.txt for contributors. * Author: Tomasz Wlostowski * * This program is free software: you can redistribute it and/or modify it @@ -33,16 +33,33 @@ void PNS_TUNE_STATUS_POPUP::UpdateStatus( PNS::ROUTER* aRouter ) SetText( placer->TuningInfo( m_frame->GetUserUnits() ) ); + // Determine the background color first and choose a contrasting value + COLOR4D bg( m_panel->GetBackgroundColour() ); + double h, s, l; + bg.ToHSL( h, s, l ); + switch( placer->TuningStatus() ) { case PNS::MEANDER_PLACER::TUNED: - SetTextColor( wxColour( 0, 255, 0 ) ); + if( l < 0.5 ) + SetTextColor( wxColor( 0, 255, 0 ) ); + else + SetTextColor( wxColor( 0, 128, 0 ) ); + break; case PNS::MEANDER_PLACER::TOO_SHORT: - SetTextColor( wxColour( 255, 128, 128 ) ); + if( l < 0.5 ) + SetTextColor( wxColor( 255, 128, 128 ) ); + else + SetTextColor( wxColor( 128, 64, 64 ) ); + break; case PNS::MEANDER_PLACER::TOO_LONG: - SetTextColor( wxColour( 128, 128, 255 ) ); + if( l < 0.5 ) + SetTextColor( wxColor( 24, 24, 255 ) ); + else + SetTextColor( wxColor( 19, 19, 195 ) ); + break; } } diff --git a/qa/common/test_color4d.cpp b/qa/common/test_color4d.cpp index 9d9354c4a2..0afeb18d76 100644 --- a/qa/common/test_color4d.cpp +++ b/qa/common/test_color4d.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2018 KiCad Developers, see CHANGELOG.TXT for contributors. + * Copyright (C) 2018-2019 KiCad Developers, see CHANGELOG.TXT for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -217,16 +217,70 @@ struct FROM_HSV_TO_HEX_CASE BOOST_AUTO_TEST_CASE( FromHsv ) { static const std::vector cases = { - { 90.0, 0.5, 0.5, 96, 128, 64 }, + { 10, 0.71, 0.66, 168, 69, 49 }, + { 15, 0.96, 0.34, 87, 24, 3 }, + { 120, 0.50, 0.50, 64, 128, 64 }, + { 190, 0.32, 0.97, 168, 234, 247 }, + { 240, 0.15, 0.75, 163, 163, 191 }, + { 240, 0.90, 0.75, 19, 19, 191 }, + { 310, 0.71, 0.66, 168, 49, 148 }, + { 331, 0.15, 0.85, 217, 184, 200 }, }; for( const auto& c : cases ) { auto col = COLOR4D{}; col.FromHSV( c.h, c.s, c.v ); + double new_h, new_s, new_v; + col.ToHSV( new_h, new_s, new_v ); const unsigned char alpha = 0xFF; BOOST_CHECK_PREDICATE( pred_colour_is_near_hex, ( col )( c.r )( c.g )( c.b )( alpha ) ); + BOOST_CHECK_CLOSE( c.h, new_h, 0.0001 ); + BOOST_CHECK_CLOSE( c.s, new_s, 0.0001 ); + BOOST_CHECK_CLOSE( c.v, new_v, 0.0001 ); + } +} + +struct FROM_HSL_TO_HEX_CASE +{ + double h; + double s; + double l; + unsigned char r; + unsigned char g; + unsigned char b; +}; + + +/** + * Check FromHSL + */ +BOOST_AUTO_TEST_CASE( FromHsl ) +{ + static const std::vector cases = { + { 10, 0.71, 0.66, 230, 127, 107 }, + { 15, 0.96, 0.34, 170, 45, 3 }, + { 120, 0.5, 0.5, 64, 191, 64 }, + { 190, 0.32, 0.97, 245, 249, 250 }, + { 240, 0.15, 0.75, 182, 182, 201 }, + { 240, 0.90, 0.75, 134, 134, 249 }, + { 310, 0.71, 0.66, 230, 107, 209 }, + { 331, 0.15, 0.85, 222, 211, 217 }, + }; + + for( const auto& c : cases ) + { + auto col = COLOR4D{}; + col.FromHSL( c.h, c.s, c.l ); + double new_h, new_s, new_l; + col.ToHSL( new_h, new_s, new_l ); + const unsigned char alpha = 0xFF; + + BOOST_CHECK_PREDICATE( pred_colour_is_near_hex, ( col )( c.r )( c.g )( c.b )( alpha ) ); + BOOST_CHECK_CLOSE( c.h, new_h, 0.0001 ); + BOOST_CHECK_CLOSE( c.s, new_s, 0.0001 ); + BOOST_CHECK_CLOSE( c.l, new_l, 0.0001 ); } }