1 /*
2  * Copyright (C) 2007, 2009 Toni Corvera
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18 
19 // $Id: colour.h 1211 2009-04-24 19:06:20Z  $
20 
21 #ifndef ONET_COLOUR_H
22 #define ONET_COLOUR_H
23 
24 #include <iostream>
25 
26 #ifdef _MSC_VER
27 #	include "../msvc/inttypes.h"
28 #else
29 #	include <inttypes.h>
30 #endif
31 
32 #include "../utils/constraints.hxx"
33 
34 namespace net_outlyer {
35 
36     /*!
37      * \brief Representation of an RGB colour.
38      * There are different implementations of RGB colours, the two most
39      * common being an 8bit integer per component one and a floating
40      * point in the range 0..1 per component.
41      * This class tries to abstract their differences: the aforementioned
42      * types match
43      * \code colour_type<uint8_t, 255> \endcode (but see remarks)
44      * and \code colour_type<double, 1> \endcode
45      * \remarks The range validity is deliberately left out of the
46      *          constructors, users are allowed to go beyond the bounds
47      * \remarks As this class contains (optional) checks for ranges, the
48      *          integer 0..255 representation may throw warnings when
49      *          implemented over an 8bit type (can't be out of range =>
50      *          check is always false).
51      * \remarks Conversion between representations is provided in the convert()
52      *          method, see its documentation for details on performance though.
53      * \param T_component Type of the colour components
54      * \param C_max Value of the full component (maximum colour intensity)
55      */
56     template<typename T_component, int C_max>
57     struct colour_type {
58     public:
59         T_component r, //!< Red component
60                     g, //!< Green component
61                     b; //!< Blue component
62 
63         typedef T_component value_type;
64 
65         /*!
66          * \brief Standard constructor.
67          * \param[in] r_ Red component
68          * \param[in] g_ Green component
69          * \param[in] b_ Blue component
70          * \sa check_bounds()
71          */
colour_typecolour_type72         colour_type(T_component r_, T_component g_, T_component b_)
73             : r(r_), g(g_), b(b_)
74         {
75             constraints::static_casts_to<T_component, double>();
76         }
77 
78         /*!
79          * \brief Gray-scale constructor.
80          * Colours in the gray-scale are colours with all three components
81          * equal. Such colours represent levels of gray.
82          * \param[in] gl Gray level
83          * \sa check_bounds()
84          */
colour_typecolour_type85         explicit colour_type(T_component gl)
86             : r(gl), g(gl), b(gl)
87         {
88             constraints::static_casts_to<T_component, double>();
89         }
90 
91         /*!
92          * \brief Default constructor.
93          * Initialises the colour components to 0.
94          */
colour_typecolour_type95         colour_type()
96             : r(0), g(0), b(0)
97         {
98             constraints::static_casts_to<T_component, double>();
99         }
100 
101         /*!
102          * \brief Copy constructor.
103          * \remarks Different colour implementations can't be directy copied,
104          *          but they can through conversion, e.g.:
105          * \code
106          * colour_type<double, 1>   dcol;
107          * ...
108          * colour_type<int, 255> icol( dcol.convert<int, 255>() );
109          * \endcode
110          */
colour_typecolour_type111         colour_type(const colour_type<T_component, C_max> & o)
112             : r(o.r), g(o.g), b(o.b)
113         {}
114 
115         /*!
116          * \brief Assignment operator.
117          * \pre  <code> THIS = *this and O = o </code>
118          * \post <code> *this == O </code>
119          */
120         colour_type<T_component, C_max> & operator=(const colour_type<T_component, C_max> & o) {
121             if (this == &o) { return *this; }
122 
123             r = o.r, g = o.g, b = o.b;
124             return * this;
125         }
126 
127         /*!
128          * \brief Equivalence operator.
129          * \remarks Note that the strength of this comparison depends on the
130          *          strength of \p T_component 's comparison operator (e.g.
131          *          two real-world equivalent doubles might not be equivalent
132          *          to the machine).
133          */
134         bool operator==(const colour_type<T_component, C_max> & c) const {
135             if (this == &c) { return true; }
136 
137             return c.r = r && c.g == c.g && c.b == c.b;
138         }
139 
140         /*!
141          * \brief Inequivalence operator.
142          * \pre  <code> THIS = *this AND C = c </code>
143          * \post <code> THIS != C  ==  !(THIS == C) </code>
144          */
145         bool operator!=(const colour_type<T_component, C_max> & c) const {
146             return !(*this == c);
147         }
148 
149         /*!
150          * \brief Conversion.
151          * This method converts between implementations.
152          * \remarks This method uses floating point intermediate values to do
153          *          the conversion, so although <code>O(1)</code>, it isn't
154          *          completely cheap. This is of special interest when
155          *          the converted colour's component must be accesed, in which
156          *          case it's better to hold the conversion in a temporal
157          *          variable.
158          * \param T_C_component Type of the target colour components
159          * \param C_C_max Value of the target's full component (maximum colour
160          *                intensity)
161          * \post Let \p T1 and \p T2 be scalar types with perfect == and !=
162          *       operators, \p X, \p Y integer values, and either
163          *       \p T1!=T2 or \p X!=Y (or both):
164          *
165          *      \code
166          * colour_type<T1, X> c1, d;
167          * colour_type<T2, Y> c2;
168          * ...
169          * c2 = c1.convert<T2, Y>();
170          * d = c2.convert<T1, X>();
171          * // d and c1 must be equal
172          * assert( d == c1 );
173          *      \endcode
174          * \todo FIXME: There's a problem with long double components
175          */
176         template<typename T_C_component, int C_C_max>
convertcolour_type177         colour_type<T_C_component, C_C_max> convert() const {
178             // Need to be sure the intermediates are FP:
179             const double
180                 r_ = static_cast<double>(r) / C_max * C_C_max,
181                 g_ = static_cast<double>(g) / C_max * C_C_max,
182                 b_ = static_cast<double>(b) / C_max * C_C_max;
183 
184             return colour_type<T_C_component, C_C_max>(
185                                static_cast<T_C_component>(r_),
186                                static_cast<T_C_component>(g_),
187                                static_cast<T_C_component>(b_)
188                    );
189         }
190     }; // struct colour_type
191 
192     // Forward declarations of common output operators
193 
194     // FP implementations
195     std::ostream & operator<<(std::ostream &, const colour_type<long double,1> &);
196     std::ostream & operator<<(std::ostream &, const colour_type<double,1> &);
197     std::ostream & operator<<(std::ostream &, const colour_type<float,1> &);
198 
199     // Integer implementations
200     std::ostream & operator<<(std::ostream &, const colour_type<uint8_t,255> &);
201     std::ostream & operator<<(std::ostream &, const colour_type<signed short,255> &);
202     std::ostream & operator<<(std::ostream &, const colour_type<unsigned short,255> &);
203     std::ostream & operator<<(std::ostream &, const colour_type<signed int,255> &);
204     std::ostream & operator<<(std::ostream &, const colour_type<unsigned int,255> &);
205 
206     typedef colour_type<double,1> rgb_type;
207 
208     /*!
209      * \brief HSV colour representation
210      * \note Code based on <http://www.deadbeef.com/index.php/converting_rgb_to_hsv_in_c>
211      * and <http://www.codeproject.com/KB/miscctrl/CPicker.aspx>
212      * Hue is 0..360
213      * Saturation is 0..100
214      * Value is 0..100
215      * \since 0.5.1
216      */
217     struct hsv_colour {
218     public:
219         double h, s, v;
220 
221         // TODO: Implement conversion to RGB
222         // It is pretty tricky, so won't implement unless absolutely needed
hsv_colourhsv_colour223         hsv_colour(const colour_type<double,1>& c) {
224 
225             const double maxC = std::max(std::max(c.r, c.g), c.b);
226             const double minC = std::min(std::min(c.r, c.g), c.b);
227             const double delta = maxC - minC;
228 
229             if (0 == delta) {
230                 h = 0;
231                 s = 0;
232                 v = maxC;
233             }
234             else {
235                 v = maxC * 100.0;
236                 s = (delta / maxC) * 100.0;
237 
238                 const double dR = 60*(maxC - c.r)/delta + 180;
239                 const double dG = 60*(maxC - c.g)/delta + 180;
240                 const double dB = 60*(maxC - c.b)/delta + 180;
241 
242                 if (maxC == c.r) {
243                     h = dB - dG;
244                 }
245                 else if (maxC == c.g) {
246                     h = 120 + dR - dB;
247                 }
248                 else {
249                     h = 240 + dG - dR;
250                 }
251 
252                 if (0 > h) {
253                     h += 360.0;
254                 }
255                 else if (h >= 360) {
256                     h -= 360.0;
257                 }
258             }
259         }
260     }; // hsv_colour
261 
262 } // namespace net_outlyer
263 
264 #endif // ONET_COLOUR_H
265 // vim:set ts=4 et ai:
266 
267