kicad/qa/common/test_color4d.cpp

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()