1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2018-2019 KiCad Developers, see AUTHORS.TXT for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
22  */
23 
24 #include <boost/test/unit_test.hpp>
25 
26 #include "color4d_test_utils.h"
27 
28 #include <qa_utils/wx_utils/unit_test_utils.h>
29 
30 #include <gal/color4d.h>
31 
32 #ifdef WX_COMPATIBILITY
33 #include <wx/colour.h>
34 #endif
35 
36 // All these tests are of a class in KIGFX
37 using namespace KIGFX;
38 
39 
40 /**
41  * Declares a struct as the Boost test fixture.
42  */
43 BOOST_AUTO_TEST_SUITE( Color4D )
44 
45 
46 /**
47  * Check basic setting and getting of values
48  */
BOOST_AUTO_TEST_CASE(BasicOps)49 BOOST_AUTO_TEST_CASE( BasicOps )
50 {
51     const auto c = COLOR4D{ 0.4, 0.5, 0.6, 0.7 };
52 
53     BOOST_CHECK_EQUAL( c.r, 0.4 );
54     BOOST_CHECK_EQUAL( c.g, 0.5 );
55     BOOST_CHECK_EQUAL( c.b, 0.6 );
56     BOOST_CHECK_EQUAL( c.a, 0.7 );
57 
58     const auto copied = c;
59 
60     // Test equality
61     BOOST_CHECK_EQUAL( c, copied );
62 
63     const auto c2 = COLOR4D{ 0.1, 0.2, 0.3, 0.4 };
64 
65     // Test inequality
66     BOOST_CHECK_NE( c, c2 );
67 }
68 
69 
70 /**
71  * Test case data for a test that takes a colour and a scalar factor
72  * and returns a result.
73  */
74 struct COLOR_SCALAR_CASE
75 {
76     COLOR4D start;
77     double  factor;
78     COLOR4D expected;
79 };
80 
81 
82 /**
83  * Check inversion
84  */
BOOST_AUTO_TEST_CASE(Invert)85 BOOST_AUTO_TEST_CASE( Invert )
86 {
87     // Inverts RGB, A is the same
88     static const std::vector<COLOR_SCALAR_CASE> cases = {
89         { { 0.0, 0.25, 1.0, 1.0 }, 0.0, { 1.0, 0.75, 0.0, 1.0 } },
90     };
91 
92     for( const auto& c : cases )
93     {
94         auto col = c.start;
95 
96         const auto inverted = col.Inverted();
97         BOOST_CHECK_EQUAL( inverted, c.expected );
98 
99         // Test in-place function
100         col.Invert();
101         BOOST_CHECK_EQUAL( col, c.expected );
102     }
103 }
104 
105 
106 /**
107  * Check inversion
108  */
BOOST_AUTO_TEST_CASE(Brighten)109 BOOST_AUTO_TEST_CASE( Brighten )
110 {
111     static const std::vector<COLOR_SCALAR_CASE> cases = {
112         { { 0.0, 0.0, 0.0, 1.0 }, 0.5, { 0.5, 0.5, 0.5, 1.0 } },
113         { { 0.0, 0.5, 1.0, 1.0 }, 0.5, { 0.5, 0.75, 1.0, 1.0 } },
114     };
115 
116     for( const auto& c : cases )
117     {
118         auto col = c.start;
119 
120         const auto brightened = col.Brightened( c.factor );
121         BOOST_CHECK_EQUAL( brightened, c.expected );
122 
123         // Test in-place function
124         col.Brighten( c.factor );
125         BOOST_CHECK_EQUAL( col, c.expected );
126     }
127 }
128 
129 
130 /**
131  * Check darken
132  */
BOOST_AUTO_TEST_CASE(Darken)133 BOOST_AUTO_TEST_CASE( Darken )
134 {
135     static const std::vector<COLOR_SCALAR_CASE> cases = {
136         { { 0.0, 0.0, 0.0, 1.0 }, 0.5, { 0.0, 0.0, 0.0, 1.0 } },
137         { { 1.0, 1.0, 1.0, 1.0 }, 0.5, { 0.5, 0.5, 0.5, 1.0 } },
138     };
139 
140     for( const auto& c : cases )
141     {
142         auto col = c.start;
143 
144         const auto brightened = col.Darkened( c.factor );
145         BOOST_CHECK_EQUAL( brightened, c.expected );
146 
147         // Test in-place function
148         col.Darken( c.factor );
149         BOOST_CHECK_EQUAL( col, c.expected );
150     }
151 }
152 
153 
154 /**
155  * Check alpha setting
156  */
BOOST_AUTO_TEST_CASE(WithAlpha)157 BOOST_AUTO_TEST_CASE( WithAlpha )
158 {
159     static const std::vector<COLOR_SCALAR_CASE> cases = {
160         { { 0.0, 0.0, 0.0, 1.0 }, 0.5, { 0.0, 0.0, 0.0, 0.5 } },
161         { { 0.0, 0.5, 1.0, 1.0 }, 0.5, { 0.0, 0.5, 1.0, 0.5 } },
162     };
163 
164     for( const auto& c : cases )
165     {
166         auto col = c.start;
167 
168         const auto with_alpha = col.WithAlpha( c.factor );
169         BOOST_CHECK_EQUAL( with_alpha, c.expected );
170     }
171 
172     // Note: If COLOR4D::WithAlpha raised an exception, we could check
173     // the bounds-checking with BOOST_REQUIRE_THROW,
174     // but it assert()s, so we can't.
175 }
176 
177 struct FROM_HSV_TO_HEX_CASE
178 {
179     double        h;
180     double        s;
181     double        v;
182     unsigned char r;
183     unsigned char g;
184     unsigned char b;
185 };
186 
187 
188 /**
189  * Check FromHSV
190  */
BOOST_AUTO_TEST_CASE(FromHsv)191 BOOST_AUTO_TEST_CASE( FromHsv )
192 {
193     static const std::vector<FROM_HSV_TO_HEX_CASE> cases = {
194         {  10, 0.71, 0.66, 168,  69,  49 },
195         {  15, 0.96, 0.34,  87,  24,   3 },
196         { 120, 0.50, 0.50,  64, 128,  64 },
197         { 190, 0.32, 0.97, 168, 234, 247 },
198         { 240, 0.15, 0.75, 163, 163, 191 },
199         { 240, 0.90, 0.75,  19,  19, 191 },
200         { 310, 0.71, 0.66, 168,  49, 148 },
201         { 331, 0.15, 0.85, 217, 184, 200 },
202     };
203 
204     for( const auto& c : cases )
205     {
206         auto col = COLOR4D{};
207         col.FromHSV( c.h, c.s, c.v );
208         double new_h, new_s, new_v;
209         col.ToHSV( new_h, new_s, new_v );
210         const unsigned char alpha = 0xFF;
211 
212         BOOST_CHECK_PREDICATE( KI_TEST::IsColorNearHex, ( col )( c.r )( c.g )( c.b )( alpha ) );
213         BOOST_CHECK_CLOSE( c.h, new_h, 0.0001 );
214         BOOST_CHECK_CLOSE( c.s, new_s, 0.0001 );
215         BOOST_CHECK_CLOSE( c.v, new_v, 0.0001 );
216     }
217 }
218 
219 struct FROM_HSL_TO_HEX_CASE
220 {
221     double        h;
222     double        s;
223     double        l;
224     unsigned char r;
225     unsigned char g;
226     unsigned char b;
227 };
228 
229 
230 /**
231  * Check FromHSL
232  */
BOOST_AUTO_TEST_CASE(FromHsl)233 BOOST_AUTO_TEST_CASE( FromHsl )
234 {
235     static const std::vector<FROM_HSL_TO_HEX_CASE> cases = {
236         {  10, 0.71, 0.66, 230, 127, 107 },
237         {  15, 0.96, 0.34, 170,  45,   3 },
238         { 120, 0.5,  0.5,   64, 191,  64 },
239         { 190, 0.32, 0.97, 245, 249, 250 },
240         { 240, 0.15, 0.75, 182, 182, 201 },
241         { 240, 0.90, 0.75, 134, 134, 249 },
242         { 310, 0.71, 0.66, 230, 107, 209 },
243         { 331, 0.15, 0.85, 222, 211, 217 },
244     };
245 
246     for( const auto& c : cases )
247     {
248         auto col = COLOR4D{};
249         col.FromHSL( c.h, c.s, c.l );
250         double new_h, new_s, new_l;
251         col.ToHSL( new_h, new_s, new_l );
252         const unsigned char alpha = 0xFF;
253 
254         BOOST_CHECK_PREDICATE( KI_TEST::IsColorNearHex, ( col )( c.r )( c.g )( c.b )( alpha ) );
255         BOOST_CHECK_CLOSE( c.h, new_h, 0.0001 );
256         BOOST_CHECK_CLOSE( c.s, new_s, 0.0001 );
257         BOOST_CHECK_CLOSE( c.l, new_l, 0.0001 );
258     }
259 }
260 
261 
262 #ifdef WX_COMPATIBILITY
263 
264 struct WX_CONV_CASE
265 {
266     wxColour wx;
267     COLOR4D  c4d;
268 };
269 
270 
271 static std::vector<WX_CONV_CASE> wx_conv_cases = {
272     { { 0x00, 0x00, 0x00, 0x00 }, { 0.0, 0.0, 0.0, 0.0 } },
273     { { 0x66, 0x80, 0x99, 0xB3 }, { 0.4, 0.5, 0.6, 0.7 } },
274     { { 0xFF, 0xFF, 0xFF, 0xFF }, { 1.0, 1.0, 1.0, 1.0 } },
275     { { 0xFF, 0x00, 0x00, 0xFF }, { 0.999, 0.001, 0.0, 1.0 } },
276 };
277 
278 
279 /**
280  * Check conversion to WxColour
281  */
BOOST_AUTO_TEST_CASE(ToWx)282 BOOST_AUTO_TEST_CASE( ToWx )
283 {
284     for( const auto& c : wx_conv_cases )
285     {
286         wxColour wx_col = c.c4d.ToColour();
287 
288         // A hack, but avoids having to define a custom operator<<
289         BOOST_CHECK_EQUAL( wx_col.Red(), c.wx.Red() );
290         BOOST_CHECK_EQUAL( wx_col.Green(), c.wx.Green() );
291         BOOST_CHECK_EQUAL( wx_col.Blue(), c.wx.Blue() );
292         BOOST_CHECK_EQUAL( wx_col.Alpha(), c.wx.Alpha() );
293     }
294 }
295 
296 
297 /**
298  * Check conversion from WxColour
299  */
BOOST_AUTO_TEST_CASE(FromWx)300 BOOST_AUTO_TEST_CASE( FromWx )
301 {
302     const double tol = 0.5 / 255.0; // One bit of quantised error
303 
304     for( const auto& c : wx_conv_cases )
305     {
306         const auto col = COLOR4D{ c.wx };
307 
308         BOOST_CHECK_PREDICATE( KI_TEST::IsColorNear, ( col )( c.c4d )( tol ) );
309     }
310 }
311 
312 #endif // WX_COMPATIBILITY
313 
314 BOOST_AUTO_TEST_SUITE_END()
315