File indexing completed on 2024-05-12 15:59:34

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