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