Warning, file /office/calligra/libs/pigment/KoColorSpaceMaths.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *  Copyright (c) 2006,2007,2010 Cyrille Berger <cberger@cberger.bet
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #ifndef KOCOLORSPACEMATHS_H_
0021 #define KOCOLORSPACEMATHS_H_
0022 
0023 #include <cmath>
0024 #include <limits>
0025 
0026 #include "pigment_export.h"
0027 #include <KoIntegerMaths.h>
0028 #include "KoChannelInfo.h"
0029 #include "KoLut.h"
0030 
0031 #undef _T
0032 
0033 /**
0034  * This is an empty mainWindow that needs to be "specialized" for each possible
0035  * numerical type (quint8, quint16...).
0036  *
0037  * It needs to defines some static constant fields :
0038  * - zeroValue : the zero for this numerical type
0039  * - unitValue : the maximum value of the normal dynamic range
0040  * - max : the maximum value
0041  * - min : the minimum value
0042  * - epsilon : a value close to zero but different of zero
0043  * - bits : the bit depth
0044  *
0045  * And some types :
0046  * - compositetype the type used for composite operations (usually one with
0047  *   a higher bit depth)
0048  */
0049 template<typename _T>
0050 class KoColorSpaceMathsTraits
0051 {
0052 public:
0053 };
0054 
0055 template<>
0056 class PIGMENTCMS_EXPORT KoColorSpaceMathsTraits<quint8>
0057 {
0058 public:
0059     typedef qint32 compositetype;
0060     static const quint8 zeroValue = 0;
0061     static const quint8 unitValue = 0x00FF;
0062     static const quint8 halfValue = 0x00FF / 2;
0063     static const quint8 max = 0x00FF;
0064     static const quint8 min = 0;
0065     static const quint8 epsilon = 1;
0066     static const qint8 bits = 8;
0067     static const KoChannelInfo::enumChannelValueType channelValueType;
0068 };
0069 
0070 template<>
0071 class PIGMENTCMS_EXPORT KoColorSpaceMathsTraits<quint16>
0072 {
0073 public:
0074     typedef qint64 compositetype;
0075     static const quint16 zeroValue = 0;
0076     static const quint16 unitValue = 0xFFFF;
0077     static const quint16 halfValue = 0xFFFF / 2;
0078     static const quint16 max = 0xFFFF;
0079     static const quint16 min = 0;
0080     static const quint16 epsilon = 1;
0081     static const qint8 bits = 16;
0082     static const KoChannelInfo::enumChannelValueType channelValueType;
0083 };
0084 
0085 template<>
0086 class PIGMENTCMS_EXPORT KoColorSpaceMathsTraits<qint16>
0087 {
0088 public:
0089     typedef qint64 compositetype;
0090     static const qint16 zeroValue = 0;
0091     static const qint16 unitValue = 32767;
0092     static const qint16 halfValue = 32767 / 2;
0093     static const qint16 max = 32767;
0094     static const qint16 min = -32768;
0095     static const qint16 epsilon = 1;
0096     static const qint8 bits = 16;
0097     static const KoChannelInfo::enumChannelValueType channelValueType;
0098 };
0099 
0100 template<>
0101 class PIGMENTCMS_EXPORT KoColorSpaceMathsTraits<quint32>
0102 {
0103 public:
0104     typedef qint64 compositetype;
0105     static const quint32 zeroValue = 0;
0106     static const quint32 unitValue = 0xFFFFFFFF;
0107     static const quint32 halfValue = 0xFFFFFFFF / 2;
0108     static const quint32 max = 0xFFFFFFFF;
0109     static const quint32 min = 0;
0110     static const quint32 epsilon = 1;
0111     static const qint8 bits = 32;
0112     static const KoChannelInfo::enumChannelValueType channelValueType;
0113 };
0114 
0115 #include <KoConfig.h>
0116 #ifdef HAVE_OPENEXR
0117 #include <half.h>
0118 
0119 template<>
0120 class PIGMENTCMS_EXPORT KoColorSpaceMathsTraits<half>
0121 {
0122 public:
0123     typedef double compositetype;
0124     static const half zeroValue;
0125     static const half unitValue;
0126     static const half halfValue;
0127     static const half max;
0128     static const half min;
0129     static const half epsilon;
0130     static const qint8 bits = 16;
0131     static const KoChannelInfo::enumChannelValueType channelValueType;
0132 };
0133 #endif
0134 
0135 template<>
0136 class PIGMENTCMS_EXPORT KoColorSpaceMathsTraits<float>
0137 {
0138 public:
0139     typedef double compositetype;
0140     static const float zeroValue;
0141     static const float unitValue;
0142     static const float halfValue;
0143     static const float max;
0144     static const float min;
0145     static const float epsilon;
0146     static const qint8 bits = 32;
0147     static const KoChannelInfo::enumChannelValueType channelValueType;
0148 };
0149 
0150 template<>
0151 class PIGMENTCMS_EXPORT KoColorSpaceMathsTraits<double>
0152 {
0153 public:
0154     typedef double compositetype;
0155     static const double zeroValue;
0156     static const double unitValue;
0157     static const double halfValue;
0158     static const double max;
0159     static const double min;
0160     static const double epsilon;
0161     static const qint8 bits = 64;
0162     static const KoChannelInfo::enumChannelValueType channelValueType;
0163 };
0164 
0165 #ifdef Q_CC_MSVC
0166 // MSVC do not have lrint
0167 
0168 const double _double2fixmagic = 68719476736.0*1.5;
0169 const qint32 _shiftamt        = 16;                    //16.16 fixed point representation,
0170 
0171 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
0172         #define iexp_                           0
0173         #define iman_                           1
0174 #else
0175         #define iexp_                           1
0176         #define iman_                           0
0177 #endif //BigEndian_
0178 
0179 inline int float2int(double val)
0180 {
0181     val = val + _double2fixmagic;
0182     return ((int*)&val)[iman_] >> _shiftamt; 
0183 }
0184 
0185 inline int float2int(float val)
0186 {
0187     return float2int((double)val);
0188 }
0189 
0190 #else
0191 
0192 inline int float2int(float x)
0193 {
0194     return lrintf(x);
0195 }
0196 
0197 inline int float2int(double x)
0198 {
0199     return lrint(x);
0200 }
0201 
0202 #endif
0203 
0204 template<typename _T_>
0205 struct KoIntegerToFloat {
0206   inline float operator()(_T_ f) const
0207   {
0208     return f / float(KoColorSpaceMathsTraits<_T_>::max);
0209   }
0210 };
0211 
0212 struct KoLuts {
0213 
0214   static PIGMENTCMS_EXPORT const Ko::FullLut< KoIntegerToFloat<quint16>, float, quint16> Uint16ToFloat;
0215   static PIGMENTCMS_EXPORT const Ko::FullLut< KoIntegerToFloat<quint8>, float, quint8> Uint8ToFloat;
0216 };
0217 
0218 /**
0219  * This class defines some elementary operations used by various color
0220  * space. It's intended to be generic, but some specialization exists
0221  * either for optimization or just for being buildable.
0222  *
0223  * @param _T some numerical type with an existing trait
0224  * @param _Tdst some other numerical type with an existing trait, it is
0225  *              only needed if different of _T
0226  */
0227 template < typename _T, typename _Tdst = _T >
0228 class KoColorSpaceMaths
0229 {
0230     typedef KoColorSpaceMathsTraits<_T> traits;
0231     typedef typename traits::compositetype src_compositetype;
0232     typedef typename KoColorSpaceMathsTraits<_Tdst>::compositetype dst_compositetype;
0233     
0234 public:
0235     inline static _Tdst multiply(_T a, _Tdst b) {
0236         return (dst_compositetype(a)*b) /  KoColorSpaceMathsTraits<_Tdst>::unitValue;
0237     }
0238     
0239     inline static _Tdst multiply(_T a, _Tdst b, _Tdst c) {
0240         return (dst_compositetype(a)*b*c) / (dst_compositetype(KoColorSpaceMathsTraits<_Tdst>::unitValue) * KoColorSpaceMathsTraits<_T>::unitValue);
0241     }
0242 
0243     /**
0244      * Division : (a * MAX ) / b
0245      * @param a
0246      * @param b
0247      */
0248     inline static dst_compositetype divide(_T a, _Tdst b) {
0249         return (dst_compositetype(a) *  KoColorSpaceMathsTraits<_Tdst>::unitValue) / b;
0250     }
0251     
0252     /**
0253      * Inversion : unitValue - a
0254      * @param a
0255      */
0256     inline static _T invert(_T a) {
0257         return traits::unitValue - a;
0258     }
0259 
0260     /**
0261      * Blending : (a * alpha) + b * (1 - alpha)
0262      * @param a
0263      * @param b
0264      * @param alpha
0265      */
0266     inline static _T blend(_T a, _T b, _T alpha) {
0267         src_compositetype c = ((src_compositetype(a) - b) * alpha) / traits::unitValue;
0268         return c + b;
0269     }
0270 
0271     /**
0272      * This function will scale a value of type _T to fit into a _Tdst.
0273      */
0274     inline static _Tdst scaleToA(_T a) {
0275         return _Tdst(dst_compositetype(a) * KoColorSpaceMathsTraits<_Tdst>::unitValue / KoColorSpaceMathsTraits<_T>::unitValue);
0276     }
0277 
0278     inline static dst_compositetype clamp(dst_compositetype val) {
0279         return qBound<dst_compositetype>(KoColorSpaceMathsTraits<_Tdst>::min, val, KoColorSpaceMathsTraits<_Tdst>::max);
0280     }
0281 
0282     /**
0283      * Clamps the composite type on higher border only. That is a fast path
0284      * for scale-only transformations
0285      */
0286     inline static _Tdst clampAfterScale(dst_compositetype val) {
0287         return qMin<dst_compositetype>(val, KoColorSpaceMathsTraits<_Tdst>::max);
0288     }
0289 };
0290 
0291 //------------------------------ double specialization ------------------------------//
0292 template<>
0293 inline quint8 KoColorSpaceMaths<double, quint8>::scaleToA(double a)
0294 {
0295     double v = a * 255;
0296     return float2int(CLAMP(v, 0, 255));
0297 }
0298 
0299 template<>
0300 inline double KoColorSpaceMaths<quint8, double>::scaleToA(quint8 a)
0301 {
0302     return KoLuts::Uint8ToFloat(a);
0303 }
0304 
0305 template<>
0306 inline quint16 KoColorSpaceMaths<double, quint16>::scaleToA(double a)
0307 {
0308     double v = a * 0xFFFF;
0309     return float2int(CLAMP(v, 0, 0xFFFF));
0310 }
0311 
0312 template<>
0313 inline double KoColorSpaceMaths<quint16, double>::scaleToA(quint16 a)
0314 {
0315     return KoLuts::Uint16ToFloat(a);
0316 }
0317 
0318 template<>
0319 inline double KoColorSpaceMaths<double>::clamp(double a)
0320 {
0321     return a;
0322 }
0323 
0324 //------------------------------ float specialization ------------------------------//
0325 
0326 template<>
0327 inline float KoColorSpaceMaths<double, float>::scaleToA(double a)
0328 {
0329     return (float)a;
0330 }
0331 
0332 template<>
0333 inline double KoColorSpaceMaths<float, double>::scaleToA(float a)
0334 {
0335     return a;
0336 }
0337 
0338 template<>
0339 inline quint16 KoColorSpaceMaths<float, quint16>::scaleToA(float a)
0340 {
0341     float v = a * 0xFFFF;
0342     return (quint16)float2int(CLAMP(v, 0, 0xFFFF));
0343 }
0344 
0345 template<>
0346 inline float KoColorSpaceMaths<quint16, float>::scaleToA(quint16 a)
0347 {
0348     return KoLuts::Uint16ToFloat(a);
0349 }
0350 
0351 template<>
0352 inline quint8 KoColorSpaceMaths<float, quint8>::scaleToA(float a)
0353 {
0354     float v = a * 255;
0355     return (quint8)float2int(CLAMP(v, 0, 255));
0356 }
0357 
0358 template<>
0359 inline float KoColorSpaceMaths<quint8, float>::scaleToA(quint8 a)
0360 {
0361     return KoLuts::Uint8ToFloat(a);
0362 }
0363 
0364 template<>
0365 inline float KoColorSpaceMaths<float>::blend(float a, float b, float alpha)
0366 {
0367     return (a - b) * alpha + b;
0368 }
0369 
0370 template<>
0371 inline double KoColorSpaceMaths<float>::clamp(double a)
0372 {
0373     return a;
0374 }
0375 
0376 //------------------------------ half specialization ------------------------------//
0377 
0378 #ifdef HAVE_OPENEXR
0379 
0380 template<>
0381 inline half KoColorSpaceMaths<double, half>::scaleToA(double a)
0382 {
0383     return (half)a;
0384 }
0385 
0386 template<>
0387 inline double KoColorSpaceMaths<half, double>::scaleToA(half a)
0388 {
0389     return a;
0390 }
0391 
0392 template<>
0393 inline float KoColorSpaceMaths<half, float>::scaleToA(half a)
0394 {
0395     return a;
0396 }
0397 
0398 template<>
0399 inline half KoColorSpaceMaths<float, half>::scaleToA(float a)
0400 {
0401     return (half) a;
0402 }
0403 
0404 template<>
0405 inline quint8 KoColorSpaceMaths<half, quint8>::scaleToA(half a)
0406 {
0407     half v = a * 255;
0408     return (quint8)(CLAMP(v, 0, 255));
0409 }
0410 
0411 template<>
0412 inline half KoColorSpaceMaths<quint8, half>::scaleToA(quint8 a)
0413 {
0414     return a *(1.0 / 255.0);
0415 }
0416 template<>
0417 inline quint16 KoColorSpaceMaths<half, quint16>::scaleToA(half a)
0418 {
0419     double v = a * 0xFFFF;
0420     return (quint16)(CLAMP(v, 0, 0xFFFF));
0421 }
0422 
0423 template<>
0424 inline half KoColorSpaceMaths<quint16, half>::scaleToA(quint16 a)
0425 {
0426     return a *(1.0 / 0xFFFF);
0427 }
0428 
0429 template<>
0430 inline half KoColorSpaceMaths<half, half>::scaleToA(half a)
0431 {
0432     return a;
0433 }
0434 
0435 template<>
0436 inline half KoColorSpaceMaths<half>::blend(half a, half b, half alpha)
0437 {
0438     return (a - b) * alpha + b;
0439 }
0440 
0441 template<>
0442 inline double KoColorSpaceMaths<half>::clamp(double a)
0443 {
0444     return a;
0445 }
0446 
0447 
0448 #endif
0449 
0450 //------------------------------ quint8 specialization ------------------------------//
0451 
0452 template<>
0453 inline quint8 KoColorSpaceMaths<quint8>::multiply(quint8 a, quint8 b)
0454 {
0455     return (quint8)UINT8_MULT(a, b);
0456 }
0457 
0458 
0459 template<>
0460 inline quint8 KoColorSpaceMaths<quint8>::multiply(quint8 a, quint8 b, quint8 c)
0461 {
0462     return (quint8)UINT8_MULT3(a, b, c);
0463 }
0464 
0465 template<>
0466 inline KoColorSpaceMathsTraits<quint8>::compositetype
0467 KoColorSpaceMaths<quint8>::divide(quint8 a, quint8 b)
0468 {
0469     return UINT8_DIVIDE(a, b);
0470 }
0471 
0472 template<>
0473 inline quint8 KoColorSpaceMaths<quint8>::invert(quint8 a)
0474 {
0475     return ~a;
0476 }
0477 
0478 template<>
0479 inline quint8 KoColorSpaceMaths<quint8>::blend(quint8 a, quint8 b, quint8 c)
0480 {
0481     return UINT8_BLEND(a, b, c);
0482 }
0483 
0484 //------------------------------ quint16 specialization ------------------------------//
0485 
0486 template<>
0487 inline quint16 KoColorSpaceMaths<quint16>::multiply(quint16 a, quint16 b)
0488 {
0489     return (quint16)UINT16_MULT(a, b);
0490 }
0491 
0492 template<>
0493 inline KoColorSpaceMathsTraits<quint16>::compositetype
0494 KoColorSpaceMaths<quint16>::divide(quint16 a, quint16 b)
0495 {
0496     return UINT16_DIVIDE(a, b);
0497 }
0498 
0499 template<>
0500 inline quint16 KoColorSpaceMaths<quint16>::invert(quint16 a)
0501 {
0502     return ~a;
0503 }
0504 
0505 //------------------------------ various specialization ------------------------------//
0506 
0507 
0508 // TODO: use more functions from KoIntegersMaths to do the computation
0509 
0510 /// This specialization is needed because the default implementation won't work when scaling up
0511 template<>
0512 inline quint16 KoColorSpaceMaths<quint8, quint16>::scaleToA(quint8 a)
0513 {
0514     return UINT8_TO_UINT16(a);
0515 }
0516 
0517 template<>
0518 inline quint8 KoColorSpaceMaths<quint16, quint8>::scaleToA(quint16 a)
0519 {
0520     return UINT16_TO_UINT8(a);
0521 }
0522 
0523 
0524 // Due to once again a bug in gcc, there is the need for those specialized functions:
0525 
0526 template<>
0527 inline quint8 KoColorSpaceMaths<quint8, quint8>::scaleToA(quint8 a)
0528 {
0529     return a;
0530 }
0531 
0532 template<>
0533 inline quint16 KoColorSpaceMaths<quint16, quint16>::scaleToA(quint16 a)
0534 {
0535     return a;
0536 }
0537 
0538 template<>
0539 inline float KoColorSpaceMaths<float, float>::scaleToA(float a)
0540 {
0541     return a;
0542 }
0543 
0544 namespace Arithmetic
0545 {
0546     const static qreal pi = 3.14159265358979323846;
0547     
0548     template<class T>
0549     inline T mul(T a, T b) { return KoColorSpaceMaths<T>::multiply(a, b); }
0550     
0551     template<class T>
0552     inline T mul(T a, T b, T c) { return KoColorSpaceMaths<T>::multiply(a, b, c); }
0553     
0554 //     template<class T>
0555 //     inline T mul(T a, T b) {
0556 //         typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0557 //         return T(composite_type(a) * b / KoColorSpaceMathsTraits<T>::unitValue);
0558 //     }
0559 //     
0560 //     template<class T>
0561 //     inline T mul(T a, T b, T c) {
0562 //         typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0563 //         return T((composite_type(a) * b * c) / (composite_type(KoColorSpaceMathsTraits<T>::unitValue) * KoColorSpaceMathsTraits<T>::unitValue));
0564 //     }
0565     
0566     template<class T>
0567     inline T inv(T a) { return KoColorSpaceMaths<T>::invert(a); }
0568     
0569     template<class T>
0570     inline T lerp(T a, T b, T alpha) { return KoColorSpaceMaths<T>::blend(b, a, alpha); }
0571     
0572     template<class TRet, class T>
0573     inline TRet scale(T a) { return KoColorSpaceMaths<T,TRet>::scaleToA(a); }
0574     
0575     template<class T>
0576     inline typename KoColorSpaceMathsTraits<T>::compositetype
0577     div(T a, T b) { return KoColorSpaceMaths<T>::divide(a, b); }
0578     
0579     template<class T>
0580     inline T clamp(typename KoColorSpaceMathsTraits<T>::compositetype a) {
0581         return KoColorSpaceMaths<T>::clamp(a);
0582     }
0583     
0584     template<class T>
0585     inline T min(T a, T b, T c) {
0586         b = (a < b) ? a : b;
0587         return (b < c) ? b : c;
0588     }
0589     
0590     template<class T>
0591     inline T max(T a, T b, T c) {
0592         b = (a > b) ? a : b;
0593         return (b > c) ? b : c;
0594     }
0595     
0596     template<class T>
0597     inline T zeroValue() { return KoColorSpaceMathsTraits<T>::zeroValue; }
0598     
0599     template<class T>
0600     inline T halfValue() { return KoColorSpaceMathsTraits<T>::halfValue; }
0601     
0602     template<class T>
0603     inline T unitValue() { return KoColorSpaceMathsTraits<T>::unitValue; }
0604     
0605     template<class T>
0606     inline T unionShapeOpacity(T a, T b) {
0607         typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0608         return T(composite_type(a) + b - mul(a,b));
0609     }
0610     
0611     template<class T>
0612     inline T blend(T src, T srcAlpha, T dst, T dstAlpha, T cfValue) {
0613         return mul(inv(srcAlpha), dstAlpha, dst) + mul(inv(dstAlpha), srcAlpha, src) + mul(dstAlpha, srcAlpha, cfValue);
0614     }
0615 }
0616 
0617 struct HSYType
0618 {
0619     template<class TReal>
0620     inline static TReal getLightness(TReal r, TReal g, TReal b) {
0621         return TReal(0.299)*r + TReal(0.587)*g + TReal(0.114)*b;
0622     }
0623     
0624     template<class TReal>
0625     inline static TReal getSaturation(TReal r, TReal g, TReal b) {
0626         return Arithmetic::max(r,g,b) - Arithmetic::min(r,g,b);
0627     }
0628 };
0629 
0630 struct HSIType
0631 {
0632     template<class TReal>
0633     inline static TReal getLightness(TReal r, TReal g, TReal b) {
0634         return (r + g + b) * TReal(0.33333333333333333333); // (r + g + b) / 3.0
0635     }
0636     
0637     template<class TReal>
0638     inline static TReal getSaturation(TReal r, TReal g, TReal b) {
0639         TReal max    = Arithmetic::max(r, g, b);
0640         TReal min    = Arithmetic::min(r, g, b);
0641         TReal chroma = max - min;
0642         
0643         return (chroma > std::numeric_limits<TReal>::epsilon()) ?
0644             (TReal(1.0) - min / getLightness(r, g, b)) : TReal(0.0);
0645     }
0646 };
0647 
0648 struct HSLType
0649 {
0650     template<class TReal>
0651     inline static TReal getLightness(TReal r, TReal g, TReal b) {
0652         TReal max = Arithmetic::max(r, g, b);
0653         TReal min = Arithmetic::min(r, g, b);
0654         return (max + min) * TReal(0.5);
0655     }
0656     
0657     template<class TReal>
0658     inline static TReal getSaturation(TReal r, TReal g, TReal b) {
0659         TReal max    = Arithmetic::max(r, g, b);
0660         TReal min    = Arithmetic::min(r, g, b);
0661         TReal chroma = max - min;
0662         TReal light  = (max + min) * TReal(0.5);
0663         TReal div    = TReal(1.0) - std::abs(TReal(2.0)*light - TReal(1.0));
0664         
0665         if(div > std::numeric_limits<TReal>::epsilon())
0666             return chroma / div;
0667         
0668         return TReal(1.0);
0669     }
0670 };
0671 
0672 struct HSVType
0673 {
0674     template<class TReal>
0675     inline static TReal getLightness(TReal r, TReal g, TReal b) {
0676         return Arithmetic::max(r,g,b);
0677     }
0678     
0679     template<class TReal>
0680     inline static TReal getSaturation(TReal r, TReal g, TReal b) {
0681         TReal max = Arithmetic::max(r, g, b);
0682         TReal min = Arithmetic::min(r, g, b);
0683         return (max == TReal(0.0)) ? TReal(0.0) : (max - min) / max;
0684     }
0685 };
0686 
0687 template<class TReal>
0688 TReal getHue(TReal r, TReal g, TReal b) {
0689     TReal min    = Arithmetic::min(r, g, b);
0690     TReal max    = Arithmetic::max(r, g, b);
0691     TReal chroma = max - min;
0692     
0693     TReal hue = TReal(-1.0);
0694     
0695     if(chroma > std::numeric_limits<TReal>::epsilon()) {
0696         
0697 //         return atan2(TReal(2.0)*r - g - b, TReal(1.73205080756887729353)*(g - b));
0698         
0699         if(max == r) // between yellow and magenta
0700             hue = (g - b) / chroma;
0701         else if(max == g) // between cyan and yellow
0702             hue = TReal(2.0) + (b - r) / chroma;
0703         else if(max == b) // between magenta and cyan
0704             hue = TReal(4.0) + (r - g) / chroma;
0705         
0706         if(hue < -std::numeric_limits<TReal>::epsilon())
0707             hue += TReal(6.0);
0708         
0709         hue /= TReal(6.0);
0710     }
0711     
0712 //     hue = (r == max) ? (b-g) : (g == max) ? TReal(2.0)+(r-b) : TReal(4.0)+(g-r);
0713     
0714     return hue;
0715 }
0716 
0717 template<class TReal>
0718 void getRGB(TReal& r, TReal& g, TReal& b, TReal hue) {
0719     // 0 red    -> (1,0,0)
0720     // 1 yellow -> (1,1,0)
0721     // 2 green  -> (0,1,0)
0722     // 3 cyan   -> (0,1,1)
0723     // 4 blue   -> (0,0,1)
0724     // 5 maenta -> (1,0,1)
0725     // 6 red    -> (1,0,0)
0726     
0727     if(hue < -std::numeric_limits<TReal>::epsilon()) {
0728         r = g = b = TReal(0.0);
0729         return;
0730     }
0731     
0732     int   i = int(hue * TReal(6.0));
0733     TReal x = hue * TReal(6.0) - i;
0734     TReal y = TReal(1.0) - x;
0735     
0736     switch(i % 6){
0737         case 0: { r=TReal(1.0), g=x         , b=TReal(0.0); } break;
0738         case 1: { r=y         , g=TReal(1.0), b=TReal(0.0); } break;
0739         case 2: { r=TReal(0.0), g=TReal(1.0), b=x         ; } break;
0740         case 3: { r=TReal(0.0), g=y         , b=TReal(1.0); } break;
0741         case 4: { r=x         , g=TReal(0.0), b=TReal(1.0); } break;
0742         case 5: { r=TReal(1.0), g=TReal(0.0), b=y         ; } break;
0743     }
0744 }
0745 
0746 template<class HSXType, class TReal>
0747 inline static TReal getLightness(TReal r, TReal g, TReal b) {
0748     return HSXType::getLightness(r, g, b);
0749 }
0750 
0751 template<class HSXType, class TReal>
0752 inline void addLightness(TReal& r, TReal& g, TReal& b, TReal light)
0753 {
0754     using namespace Arithmetic;
0755     
0756     r += light;
0757     g += light;
0758     b += light;
0759     
0760     TReal l = HSXType::getLightness(r, g, b);
0761     TReal n = min(r, g, b);
0762     TReal x = max(r, g, b);
0763     
0764     if(n < TReal(0.0)) {
0765         TReal iln = TReal(1.0) / (l-n);
0766         r = l + ((r-l) * l) * iln;
0767         g = l + ((g-l) * l) * iln;
0768         b = l + ((b-l) * l) * iln;
0769     }
0770     
0771     if(x > TReal(1.0) && (x-l) > std::numeric_limits<TReal>::epsilon()) {
0772         TReal il  = TReal(1.0) - l;
0773         TReal ixl = TReal(1.0) / (x - l);
0774         r = l + ((r-l) * il) * ixl;
0775         g = l + ((g-l) * il) * ixl;
0776         b = l + ((b-l) * il) * ixl;
0777     }
0778 }
0779 
0780 template<class HSXType, class TReal>
0781 inline void setLightness(TReal& r, TReal& g, TReal& b, TReal light)
0782 {
0783     addLightness<HSXType>(r,g,b, light - HSXType::getLightness(r,g,b));
0784 }
0785 
0786 template<class HSXType, class TReal>
0787 inline static TReal getSaturation(TReal r, TReal g, TReal b) {
0788     return HSXType::getSaturation(r, g, b);
0789 }
0790 
0791 template<class HSXType, class TReal>
0792 inline void setSaturation(TReal& r, TReal& g, TReal& b, TReal sat)
0793 {
0794     int   min    = 0;
0795     int   mid    = 1;
0796     int   max    = 2;
0797     TReal rgb[3] = {r, g, b};
0798     
0799     if(rgb[mid] < rgb[min]) {
0800         int tmp = min;
0801         min = mid;
0802         mid = tmp;
0803     }
0804     
0805     if(rgb[max] < rgb[mid]) {
0806         int tmp = mid;
0807         mid = max;
0808         max = tmp;
0809     }
0810     
0811     if(rgb[mid] < rgb[min]) {
0812         int tmp = min;
0813         min = mid;
0814         mid = tmp;
0815     }
0816     
0817     if((rgb[max] - rgb[min]) > TReal(0.0)) {
0818         rgb[mid] = ((rgb[mid]-rgb[min]) * sat) / (rgb[max]-rgb[min]);
0819         rgb[max] = sat;
0820         rgb[min] = TReal(0.0);
0821         
0822         r = rgb[0];
0823         g = rgb[1];
0824         b = rgb[2];
0825     }
0826     else r = g = b = TReal(0.0);
0827 }
0828 
0829 #endif