341 lines
8.6 KiB
C++
341 lines
8.6 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* 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
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, you may find one here:
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
// #include <boost/test/test_case_template.hpp> // deprecated
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include <unit_test_utils/numeric.h>
|
|
#include <unit_test_utils/unit_test_utils.h>
|
|
|
|
#include <gal/color4d.h>
|
|
|
|
#ifdef WX_COMPATIBILITY
|
|
#include <wx/colour.h>
|
|
#endif
|
|
|
|
// All these tests are of a class in KIGFX
|
|
using namespace KIGFX;
|
|
|
|
|
|
/**
|
|
* Checks if a COLOR4D is close enough to another
|
|
*/
|
|
bool pred_colour_is_near( const COLOR4D& aCol, const COLOR4D aOther, double aTol )
|
|
{
|
|
return KI_TEST::IsWithin<double>( aCol.r, aOther.r, aTol )
|
|
&& KI_TEST::IsWithin<double>( aCol.g, aOther.g, aTol )
|
|
&& KI_TEST::IsWithin<double>( aCol.b, aOther.b, aTol )
|
|
&& KI_TEST::IsWithin<double>( aCol.a, aOther.a, aTol );
|
|
}
|
|
|
|
/**
|
|
* Checks if a COLOR4D is close enough to a given RGB char value
|
|
*/
|
|
bool pred_colour_is_near_hex(
|
|
const COLOR4D& aCol, unsigned char r, unsigned char g, unsigned char b, unsigned char a )
|
|
{
|
|
const double tol = 0.5 / 255.0; // One bit of quantised error
|
|
|
|
return KI_TEST::IsWithin<double>( aCol.r, r / 255.0, tol )
|
|
&& KI_TEST::IsWithin<double>( aCol.g, g / 255.0, tol )
|
|
&& KI_TEST::IsWithin<double>( aCol.b, b / 255.0, tol )
|
|
&& KI_TEST::IsWithin<double>( aCol.a, a / 255.0, tol );
|
|
}
|
|
|
|
|
|
/**
|
|
* Declares a struct as the Boost test fixture.
|
|
*/
|
|
BOOST_AUTO_TEST_SUITE( Color4D )
|
|
|
|
|
|
/**
|
|
* Check basic setting and getting of values
|
|
*/
|
|
BOOST_AUTO_TEST_CASE( BasicOps )
|
|
{
|
|
const auto c = COLOR4D{ 0.4, 0.5, 0.6, 0.7 };
|
|
|
|
BOOST_CHECK_EQUAL( c.r, 0.4 );
|
|
BOOST_CHECK_EQUAL( c.g, 0.5 );
|
|
BOOST_CHECK_EQUAL( c.b, 0.6 );
|
|
BOOST_CHECK_EQUAL( c.a, 0.7 );
|
|
|
|
const auto copied = c;
|
|
|
|
// Test equality
|
|
BOOST_CHECK_EQUAL( c, copied );
|
|
|
|
const auto c2 = COLOR4D{ 0.1, 0.2, 0.3, 0.4 };
|
|
|
|
// Test inequality
|
|
BOOST_CHECK_NE( c, c2 );
|
|
}
|
|
|
|
|
|
/**
|
|
* Test case data for a test that takes a colour and a scalar factor
|
|
* and returns a result.
|
|
*/
|
|
struct COLOR_SCALAR_CASE
|
|
{
|
|
COLOR4D start;
|
|
double factor;
|
|
COLOR4D expected;
|
|
};
|
|
|
|
|
|
/**
|
|
* Check inversion
|
|
*/
|
|
BOOST_AUTO_TEST_CASE( Invert )
|
|
{
|
|
// Inverts RGB, A is the same
|
|
static const std::vector<COLOR_SCALAR_CASE> cases = {
|
|
{ { 0.0, 0.25, 1.0, 1.0 }, 0.0, { 1.0, 0.75, 0.0, 1.0 } },
|
|
};
|
|
|
|
for( const auto& c : cases )
|
|
{
|
|
auto col = c.start;
|
|
|
|
const auto inverted = col.Inverted();
|
|
BOOST_CHECK_EQUAL( inverted, c.expected );
|
|
|
|
// Test in-place function
|
|
col.Invert();
|
|
BOOST_CHECK_EQUAL( col, c.expected );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check inversion
|
|
*/
|
|
BOOST_AUTO_TEST_CASE( Brighten )
|
|
{
|
|
static const std::vector<COLOR_SCALAR_CASE> cases = {
|
|
{ { 0.0, 0.0, 0.0, 1.0 }, 0.5, { 0.5, 0.5, 0.5, 1.0 } },
|
|
{ { 0.0, 0.5, 1.0, 1.0 }, 0.5, { 0.5, 0.75, 1.0, 1.0 } },
|
|
};
|
|
|
|
for( const auto& c : cases )
|
|
{
|
|
auto col = c.start;
|
|
|
|
const auto brightened = col.Brightened( c.factor );
|
|
BOOST_CHECK_EQUAL( brightened, c.expected );
|
|
|
|
// Test in-place function
|
|
col.Brighten( c.factor );
|
|
BOOST_CHECK_EQUAL( col, c.expected );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check darken
|
|
*/
|
|
BOOST_AUTO_TEST_CASE( Darken )
|
|
{
|
|
static const std::vector<COLOR_SCALAR_CASE> cases = {
|
|
{ { 0.0, 0.0, 0.0, 1.0 }, 0.5, { 0.0, 0.0, 0.0, 1.0 } },
|
|
{ { 1.0, 1.0, 1.0, 1.0 }, 0.5, { 0.5, 0.5, 0.5, 1.0 } },
|
|
};
|
|
|
|
for( const auto& c : cases )
|
|
{
|
|
auto col = c.start;
|
|
|
|
const auto brightened = col.Darkened( c.factor );
|
|
BOOST_CHECK_EQUAL( brightened, c.expected );
|
|
|
|
// Test in-place function
|
|
col.Darken( c.factor );
|
|
BOOST_CHECK_EQUAL( col, c.expected );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check alpha setting
|
|
*/
|
|
BOOST_AUTO_TEST_CASE( WithAlpha )
|
|
{
|
|
static const std::vector<COLOR_SCALAR_CASE> cases = {
|
|
{ { 0.0, 0.0, 0.0, 1.0 }, 0.5, { 0.0, 0.0, 0.0, 0.5 } },
|
|
{ { 0.0, 0.5, 1.0, 1.0 }, 0.5, { 0.0, 0.5, 1.0, 0.5 } },
|
|
};
|
|
|
|
for( const auto& c : cases )
|
|
{
|
|
auto col = c.start;
|
|
|
|
const auto with_alpha = col.WithAlpha( c.factor );
|
|
BOOST_CHECK_EQUAL( with_alpha, c.expected );
|
|
}
|
|
|
|
// Note: If COLOR4D::WithAlpha raised an exception, we could check
|
|
// the bounds-checking with BOOST_REQUIRE_THROW,
|
|
// but it assert()s, so we can't.
|
|
}
|
|
|
|
struct FROM_HSV_TO_HEX_CASE
|
|
{
|
|
double h;
|
|
double s;
|
|
double v;
|
|
unsigned char r;
|
|
unsigned char g;
|
|
unsigned char b;
|
|
};
|
|
|
|
|
|
/**
|
|
* Check FromHSV
|
|
*/
|
|
BOOST_AUTO_TEST_CASE( FromHsv )
|
|
{
|
|
static const std::vector<FROM_HSV_TO_HEX_CASE> cases = {
|
|
{ 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<FROM_HSL_TO_HEX_CASE> 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 );
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef WX_COMPATIBILITY
|
|
|
|
struct WX_CONV_CASE
|
|
{
|
|
wxColour wx;
|
|
COLOR4D c4d;
|
|
};
|
|
|
|
|
|
static std::vector<WX_CONV_CASE> wx_conv_cases = {
|
|
{ { 0x00, 0x00, 0x00, 0x00 }, { 0.0, 0.0, 0.0, 0.0 } },
|
|
{ { 0x66, 0x80, 0x99, 0xB3 }, { 0.4, 0.5, 0.6, 0.7 } },
|
|
{ { 0xFF, 0xFF, 0xFF, 0xFF }, { 1.0, 1.0, 1.0, 1.0 } },
|
|
{ { 0xFF, 0x00, 0x00, 0xFF }, { 0.999, 0.001, 0.0, 1.0 } },
|
|
};
|
|
|
|
|
|
/**
|
|
* Check conversion to WxColour
|
|
*/
|
|
BOOST_AUTO_TEST_CASE( ToWx )
|
|
{
|
|
for( const auto& c : wx_conv_cases )
|
|
{
|
|
wxColour wx_col = c.c4d.ToColour();
|
|
|
|
// A hack, but avoids having to define a custom operator<<
|
|
BOOST_CHECK_EQUAL( wx_col.Red(), c.wx.Red() );
|
|
BOOST_CHECK_EQUAL( wx_col.Green(), c.wx.Green() );
|
|
BOOST_CHECK_EQUAL( wx_col.Blue(), c.wx.Blue() );
|
|
BOOST_CHECK_EQUAL( wx_col.Alpha(), c.wx.Alpha() );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check conversion from WxColour
|
|
*/
|
|
BOOST_AUTO_TEST_CASE( FromWx )
|
|
{
|
|
const double tol = 0.5 / 255.0; // One bit of quantised error
|
|
|
|
for( const auto& c : wx_conv_cases )
|
|
{
|
|
const auto col = COLOR4D{ c.wx };
|
|
|
|
BOOST_CHECK_PREDICATE( pred_colour_is_near, ( col )( c.c4d )( tol ) );
|
|
}
|
|
}
|
|
|
|
#endif // WX_COMPATIBILITY
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|