KoColorSpaceMaths.h 23.1 KB
Newer Older
1
/*
2
 *  Copyright (c) 2006,2007,2010 Cyrille Berger <cberger@cberger.bet
3 4
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8 9 10 11
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15 16 17 18 19 20 21 22
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#ifndef KOCOLORSPACEMATHS_H_
#define KOCOLORSPACEMATHS_H_

23 24 25
#include <cmath>
#include <limits>

26
#include "kritapigment_export.h"
27
#include <KoIntegerMaths.h>
28
#include "KoChannelInfo.h"
29
#include "KoLut.h"
30

31
#undef _T
32

Cyrille Berger's avatar
Cyrille Berger committed
33
/**
34
 * This is an empty mainWindow that needs to be "specialized" for each possible
Cyrille Berger's avatar
Cyrille Berger committed
35
 * numerical type (quint8, quint16...).
36
 *
Cyrille Berger's avatar
Cyrille Berger committed
37 38 39 40 41 42 43
 * It needs to defines some static constant fields :
 * - zeroValue : the zero for this numerical type
 * - unitValue : the maximum value of the normal dynamic range
 * - max : the maximum value
 * - min : the minimum value
 * - epsilon : a value close to zero but different of zero
 * - bits : the bit depth
44
 *
Cyrille Berger's avatar
Cyrille Berger committed
45 46 47 48
 * And some types :
 * - compositetype the type used for composite operations (usually one with
 *   a higher bit depth)
 */
49
template<typename _T>
50 51 52
class KoColorSpaceMathsTraits
{
public:
53 54 55
};

template<>
56
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<quint8>
57 58 59 60 61
{
public:
    typedef qint32 compositetype;
    static const quint8 zeroValue = 0;
    static const quint8 unitValue = 0x00FF;
62
    static const quint8 halfValue = 0x00FF / 2;
63 64 65 66 67
    static const quint8 max = 0x00FF;
    static const quint8 min = 0;
    static const quint8 epsilon = 1;
    static const qint8 bits = 8;
    static const KoChannelInfo::enumChannelValueType channelValueType;
68 69 70
};

template<>
71
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<quint16>
72 73 74 75 76
{
public:
    typedef qint64 compositetype;
    static const quint16 zeroValue = 0;
    static const quint16 unitValue = 0xFFFF;
77
    static const quint16 halfValue = 0xFFFF / 2;
78 79 80 81 82
    static const quint16 max = 0xFFFF;
    static const quint16 min = 0;
    static const quint16 epsilon = 1;
    static const qint8 bits = 16;
    static const KoChannelInfo::enumChannelValueType channelValueType;
83 84 85
};

template<>
86
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<qint16>
87 88 89 90 91
{
public:
    typedef qint64 compositetype;
    static const qint16 zeroValue = 0;
    static const qint16 unitValue = 32767;
92
    static const qint16 halfValue = 32767 / 2;
93 94 95 96 97
    static const qint16 max = 32767;
    static const qint16 min = -32768;
    static const qint16 epsilon = 1;
    static const qint8 bits = 16;
    static const KoChannelInfo::enumChannelValueType channelValueType;
98 99 100
};

template<>
101
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<quint32>
102 103 104 105 106
{
public:
    typedef qint64 compositetype;
    static const quint32 zeroValue = 0;
    static const quint32 unitValue = 0xFFFFFFFF;
107
    static const quint32 halfValue = 0xFFFFFFFF / 2;
108 109 110 111 112
    static const quint32 max = 0xFFFFFFFF;
    static const quint32 min = 0;
    static const quint32 epsilon = 1;
    static const qint8 bits = 32;
    static const KoChannelInfo::enumChannelValueType channelValueType;
113 114
};

115
#include <KoConfig.h>
116 117
#ifdef HAVE_OPENEXR
#include <half.h>
118

119
template<>
120
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<half>
121 122 123 124 125
{
public:
    typedef double compositetype;
    static const half zeroValue;
    static const half unitValue;
126
    static const half halfValue;
127 128 129 130 131
    static const half max;
    static const half min;
    static const half epsilon;
    static const qint8 bits = 16;
    static const KoChannelInfo::enumChannelValueType channelValueType;
132 133 134 135
};
#endif

template<>
136
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<float>
137 138 139 140 141
{
public:
    typedef double compositetype;
    static const float zeroValue;
    static const float unitValue;
142
    static const float halfValue;
143 144 145 146 147
    static const float max;
    static const float min;
    static const float epsilon;
    static const qint8 bits = 32;
    static const KoChannelInfo::enumChannelValueType channelValueType;
148
};
149

150
template<>
151
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<double>
152 153 154 155 156
{
public:
    typedef double compositetype;
    static const double zeroValue;
    static const double unitValue;
157
    static const double halfValue;
158 159 160 161 162
    static const double max;
    static const double min;
    static const double epsilon;
    static const qint8 bits = 64;
    static const KoChannelInfo::enumChannelValueType channelValueType;
163 164
};

165 166 167 168
#ifdef Q_CC_MSVC
// MSVC do not have lrint

const double _double2fixmagic = 68719476736.0*1.5;
169
const qint32 _shiftamt        = 16;                    //16.16 fixed point representation,
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

#if Q_BYTE_ORDER == Q_BIG_ENDIAN
        #define iexp_                           0
        #define iman_                           1
#else
        #define iexp_                           1
        #define iman_                           0
#endif //BigEndian_

inline int float2int(double val)
{
    val = val + _double2fixmagic;
    return ((int*)&val)[iman_] >> _shiftamt; 
}

inline int float2int(float val)
{
    return float2int((double)val);
}

#else

inline int float2int(float x)
{
    return lrintf(x);
}

inline int float2int(double x)
{
    return lrint(x);
}

#endif

template<typename _T_>
struct KoIntegerToFloat {
  inline float operator()(_T_ f) const
  {
    return f / float(KoColorSpaceMathsTraits<_T_>::max);
  }
};

212
struct KoLuts {
213

214 215
  static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat<quint16>, float, quint16> Uint16ToFloat;
  static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat<quint8>, float, quint8> Uint8ToFloat;
216
};
217

Cyrille Berger's avatar
Cyrille Berger committed
218 219 220
/**
 * This class defines some elementary operations used by various color
 * space. It's intended to be generic, but some specialization exists
Boudewijn Rempt's avatar
Boudewijn Rempt committed
221
 * either for optimization or just for being buildable.
222
 *
Cyrille Berger's avatar
Cyrille Berger committed
223 224 225 226
 * @param _T some numerical type with an existing trait
 * @param _Tdst some other numerical type with an existing trait, it is
 *              only needed if different of _T
 */
227 228 229
template < typename _T, typename _Tdst = _T >
class KoColorSpaceMaths
{
230
    typedef KoColorSpaceMathsTraits<_T> traits;
231 232 233
    typedef typename traits::compositetype src_compositetype;
    typedef typename KoColorSpaceMathsTraits<_Tdst>::compositetype dst_compositetype;
    
234
public:
235 236
    inline static _Tdst multiply(_T a, _Tdst b) {
        return (dst_compositetype(a)*b) /  KoColorSpaceMathsTraits<_Tdst>::unitValue;
237
    }
238
    
239
    inline static _Tdst multiply(_T a, _Tdst b, _Tdst c) {
240
        return (dst_compositetype(a)*b*c) / (dst_compositetype(KoColorSpaceMathsTraits<_Tdst>::unitValue) * KoColorSpaceMathsTraits<_T>::unitValue);
241
    }
242 243 244 245 246 247

    /**
     * Division : (a * MAX ) / b
     * @param a
     * @param b
     */
248 249
    inline static dst_compositetype divide(_T a, _Tdst b) {
        return (dst_compositetype(a) *  KoColorSpaceMathsTraits<_Tdst>::unitValue) / b;
250
    }
251
    
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
    inline static dst_compositetype modulus(_T a, _Tdst b) {
        return (dst_compositetype(a) - floor(dst_compositetype(a)/((b != (KoColorSpaceMathsTraits<_T>::zeroValue - traits::epsilon) ? b : KoColorSpaceMathsTraits<_T>::zeroValue)  + traits::epsilon))*(b + traits::epsilon));
    }

    inline static dst_compositetype xor(_T a, _Tdst b) {
        return (int (a *  std::numeric_limits<int>::max() - traits::epsilon) ^ int (b *  std::numeric_limits<int>::max() - traits::epsilon));
    }

    inline static dst_compositetype and(_T a, _Tdst b) {
        return (int (a *  std::numeric_limits<int>::max()  - traits::epsilon) & int (b *  std::numeric_limits<int>::max()  - traits::epsilon));
    }
    
    inline static dst_compositetype or(_T a, _Tdst b) {
        return (int (a *  std::numeric_limits<int>::max()  - traits::epsilon) | int (b *  std::numeric_limits<int>::max()  - traits::epsilon));
    }

268 269 270 271 272
    /**
     * Inversion : unitValue - a
     * @param a
     */
    inline static _T invert(_T a) {
273
        return traits::unitValue - a;
274
    }
275 276 277 278 279 280 281 282

    /**
     * Blending : (a * alpha) + b * (1 - alpha)
     * @param a
     * @param b
     * @param alpha
     */
    inline static _T blend(_T a, _T b, _T alpha) {
283
        src_compositetype c = ((src_compositetype(a) - b) * alpha) / traits::unitValue;
284 285 286 287 288 289 290
        return c + b;
    }

    /**
     * This function will scale a value of type _T to fit into a _Tdst.
     */
    inline static _Tdst scaleToA(_T a) {
291
        return _Tdst(dst_compositetype(a) * KoColorSpaceMathsTraits<_Tdst>::unitValue / KoColorSpaceMathsTraits<_T>::unitValue);
292 293
    }

294 295
    inline static dst_compositetype clamp(dst_compositetype val) {
        return qBound<dst_compositetype>(KoColorSpaceMathsTraits<_Tdst>::min, val, KoColorSpaceMathsTraits<_Tdst>::max);
296
    }
297 298 299 300 301 302 303 304

    /**
     * Clamps the composite type on higher border only. That is a fast path
     * for scale-only transformations
     */
    inline static _Tdst clampAfterScale(dst_compositetype val) {
        return qMin<dst_compositetype>(val, KoColorSpaceMathsTraits<_Tdst>::max);
    }
305 306
};

307
//------------------------------ double specialization ------------------------------//
308
template<>
309
inline quint8 KoColorSpaceMaths<double, quint8>::scaleToA(double a)
310
{
311
    double v = a * 255;
312
    return float2int(CLAMP(v, 0, 255));
313 314 315
}

template<>
316
inline double KoColorSpaceMaths<quint8, double>::scaleToA(quint8 a)
317
{
318
    return KoLuts::Uint8ToFloat(a);
319 320
}

321
template<>
322
inline quint16 KoColorSpaceMaths<double, quint16>::scaleToA(double a)
323
{
324
    double v = a * 0xFFFF;
325
    return float2int(CLAMP(v, 0, 0xFFFF));
326 327 328
}

template<>
329
inline double KoColorSpaceMaths<quint16, double>::scaleToA(quint16 a)
330
{
331
    return KoLuts::Uint16ToFloat(a);
332 333
}

334 335 336 337 338 339 340
template<>
inline double KoColorSpaceMaths<double>::clamp(double a)
{
    return a;
}

//------------------------------ float specialization ------------------------------//
341 342

template<>
343
inline float KoColorSpaceMaths<double, float>::scaleToA(double a)
344 345 346 347 348
{
    return (float)a;
}

template<>
349
inline double KoColorSpaceMaths<float, double>::scaleToA(float a)
350 351 352 353
{
    return a;
}

354
template<>
355
inline quint16 KoColorSpaceMaths<float, quint16>::scaleToA(float a)
356
{
357
    float v = a * 0xFFFF;
358
    return (quint16)float2int(CLAMP(v, 0, 0xFFFF));
359 360 361
}

template<>
362
inline float KoColorSpaceMaths<quint16, float>::scaleToA(quint16 a)
363
{
364
    return KoLuts::Uint16ToFloat(a);
365 366
}

367
template<>
368
inline quint8 KoColorSpaceMaths<float, quint8>::scaleToA(float a)
369 370
{
    float v = a * 255;
371
    return (quint8)float2int(CLAMP(v, 0, 255));
372 373 374
}

template<>
375
inline float KoColorSpaceMaths<quint8, float>::scaleToA(quint8 a)
376
{
377
    return KoLuts::Uint8ToFloat(a);
378 379
}

380
template<>
381
inline float KoColorSpaceMaths<float>::blend(float a, float b, float alpha)
382
{
383
    return (a - b) * alpha + b;
384 385
}

386 387 388 389 390 391
template<>
inline double KoColorSpaceMaths<float>::clamp(double a)
{
    return a;
}

392 393 394 395
//------------------------------ half specialization ------------------------------//

#ifdef HAVE_OPENEXR

396
template<>
397
inline half KoColorSpaceMaths<double, half>::scaleToA(double a)
398 399 400 401 402
{
    return (half)a;
}

template<>
403
inline double KoColorSpaceMaths<half, double>::scaleToA(half a)
404 405 406 407
{
    return a;
}

408
template<>
409
inline float KoColorSpaceMaths<half, float>::scaleToA(half a)
410 411 412 413 414
{
    return a;
}

template<>
415
inline half KoColorSpaceMaths<float, half>::scaleToA(float a)
416 417 418 419
{
    return (half) a;
}

420
template<>
421
inline quint8 KoColorSpaceMaths<half, quint8>::scaleToA(half a)
422 423 424 425 426 427
{
    half v = a * 255;
    return (quint8)(CLAMP(v, 0, 255));
}

template<>
428
inline half KoColorSpaceMaths<quint8, half>::scaleToA(quint8 a)
429
{
Cyrille Berger's avatar
Cyrille Berger committed
430
    return a *(1.0 / 255.0);
431 432
}
template<>
433
inline quint16 KoColorSpaceMaths<half, quint16>::scaleToA(half a)
434
{
435
    double v = a * 0xFFFF;
436 437 438 439
    return (quint16)(CLAMP(v, 0, 0xFFFF));
}

template<>
440
inline half KoColorSpaceMaths<quint16, half>::scaleToA(quint16 a)
441
{
Cyrille Berger's avatar
Cyrille Berger committed
442
    return a *(1.0 / 0xFFFF);
443 444
}

445
template<>
446
inline half KoColorSpaceMaths<half, half>::scaleToA(half a)
447 448 449 450
{
    return a;
}

451 452 453
template<>
inline half KoColorSpaceMaths<half>::blend(half a, half b, half alpha)
{
454
    return (a - b) * alpha + b;
455 456
}

457 458 459 460 461 462 463
template<>
inline double KoColorSpaceMaths<half>::clamp(double a)
{
    return a;
}


464 465
#endif

466 467 468
//------------------------------ quint8 specialization ------------------------------//

template<>
469
inline quint8 KoColorSpaceMaths<quint8>::multiply(quint8 a, quint8 b)
470
{
471
    return (quint8)UINT8_MULT(a, b);
472 473
}

474 475

template<>
476
inline quint8 KoColorSpaceMaths<quint8>::multiply(quint8 a, quint8 b, quint8 c)
477
{
478
    return (quint8)UINT8_MULT3(a, b, c);
479 480
}

481
template<>
482 483
inline KoColorSpaceMathsTraits<quint8>::compositetype
KoColorSpaceMaths<quint8>::divide(quint8 a, quint8 b)
484 485 486 487
{
    return UINT8_DIVIDE(a, b);
}

488 489 490 491 492 493
template<>
inline quint8 KoColorSpaceMaths<quint8>::invert(quint8 a)
{
    return ~a;
}

494 495 496 497 498 499 500 501 502
template<>
inline quint8 KoColorSpaceMaths<quint8>::blend(quint8 a, quint8 b, quint8 c)
{
    return UINT8_BLEND(a, b, c);
}

//------------------------------ quint16 specialization ------------------------------//

template<>
503
inline quint16 KoColorSpaceMaths<quint16>::multiply(quint16 a, quint16 b)
504
{
505
    return (quint16)UINT16_MULT(a, b);
506 507 508
}

template<>
509 510
inline KoColorSpaceMathsTraits<quint16>::compositetype
KoColorSpaceMaths<quint16>::divide(quint16 a, quint16 b)
511 512 513 514
{
    return UINT16_DIVIDE(a, b);
}

515 516 517 518 519 520
template<>
inline quint16 KoColorSpaceMaths<quint16>::invert(quint16 a)
{
    return ~a;
}

521 522 523 524 525 526 527
//------------------------------ various specialization ------------------------------//


// TODO: use more functions from KoIntegersMaths to do the computation

/// This specialization is needed because the default implementation won't work when scaling up
template<>
528
inline quint16 KoColorSpaceMaths<quint8, quint16>::scaleToA(quint8 a)
529 530 531 532 533
{
    return UINT8_TO_UINT16(a);
}

template<>
534
inline quint8 KoColorSpaceMaths<quint16, quint8>::scaleToA(quint16 a)
535 536 537 538 539 540 541 542
{
    return UINT16_TO_UINT8(a);
}


// Due to once again a bug in gcc, there is the need for those specialized functions:

template<>
543
inline quint8 KoColorSpaceMaths<quint8, quint8>::scaleToA(quint8 a)
544 545 546 547 548
{
    return a;
}

template<>
549
inline quint16 KoColorSpaceMaths<quint16, quint16>::scaleToA(quint16 a)
550 551 552 553 554
{
    return a;
}

template<>
555
inline float KoColorSpaceMaths<float, float>::scaleToA(float a)
556 557 558
{
    return a;
}
559

560 561 562 563 564
namespace Arithmetic
{
    const static qreal pi = 3.14159265358979323846;
    
    template<class T>
565
    inline T mul(T a, T b) { return KoColorSpaceMaths<T>::multiply(a, b); }
566 567
    
    template<class T>
568 569 570 571 572 573 574 575 576 577 578 579 580
    inline T mul(T a, T b, T c) { return KoColorSpaceMaths<T>::multiply(a, b, c); }
    
//     template<class T>
//     inline T mul(T a, T b) {
//         typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
//         return T(composite_type(a) * b / KoColorSpaceMathsTraits<T>::unitValue);
//     }
//     
//     template<class T>
//     inline T mul(T a, T b, T c) {
//         typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
//         return T((composite_type(a) * b * c) / (composite_type(KoColorSpaceMathsTraits<T>::unitValue) * KoColorSpaceMathsTraits<T>::unitValue));
//     }
581 582
    
    template<class T>
583
    inline T inv(T a) { return KoColorSpaceMaths<T>::invert(a); }
584 585 586 587 588 589 590 591 592
    
    template<class T>
    inline T lerp(T a, T b, T alpha) { return KoColorSpaceMaths<T>::blend(b, a, alpha); }
    
    template<class TRet, class T>
    inline TRet scale(T a) { return KoColorSpaceMaths<T,TRet>::scaleToA(a); }
    
    template<class T>
    inline typename KoColorSpaceMathsTraits<T>::compositetype
593
    div(T a, T b) { return KoColorSpaceMaths<T>::divide(a, b); }
594 595 596 597 598 599 600 601 602 603 604 605 606

    template<class T>
    inline typename KoColorSpaceMathsTraits<T>::compositetype
    xor(T a, T b) { return KoColorSpaceMaths<T>::xor(a, b); }

    template<class T>
    inline typename KoColorSpaceMathsTraits<T>::compositetype
    and(T a, T b) { return KoColorSpaceMaths<T>::and(a, b); }

    template<class T>
    inline typename KoColorSpaceMathsTraits<T>::compositetype
    or(T a, T b) { return KoColorSpaceMaths<T>::or(a, b); }

607 608
    template<class T>
    inline T clamp(typename KoColorSpaceMathsTraits<T>::compositetype a) {
609
        return KoColorSpaceMaths<T>::clamp(a);
610 611 612 613 614 615 616 617 618 619 620 621 622 623
    }
    
    template<class T>
    inline T min(T a, T b, T c) {
        b = (a < b) ? a : b;
        return (b < c) ? b : c;
    }
    
    template<class T>
    inline T max(T a, T b, T c) {
        b = (a > b) ? a : b;
        return (b > c) ? b : c;
    }
    
624 625 626 627 628 629 630 631 632
    template<class T>
    inline T zeroValue() { return KoColorSpaceMathsTraits<T>::zeroValue; }
    
    template<class T>
    inline T halfValue() { return KoColorSpaceMathsTraits<T>::halfValue; }
    
    template<class T>
    inline T unitValue() { return KoColorSpaceMathsTraits<T>::unitValue; }
    
633
    template<class T>
634
    inline T unionShapeOpacity(T a, T b) {
635 636 637 638 639 640 641 642
        typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
        return T(composite_type(a) + b - mul(a,b));
    }
    
    template<class T>
    inline T blend(T src, T srcAlpha, T dst, T dstAlpha, T cfValue) {
        return mul(inv(srcAlpha), dstAlpha, dst) + mul(inv(dstAlpha), srcAlpha, src) + mul(dstAlpha, srcAlpha, cfValue);
    }
643 644 645 646 647 648 649

    template<class T>
    inline T epsilon() { return KoColorSpaceMathsTraits<T>::epsilon; }
    
    template<class T>
    inline typename KoColorSpaceMathsTraits<T>::compositetype
    mod(T a, T b) { return KoColorSpaceMaths<T>::modulus(a, b); }    
650 651
}

652
struct HSYType
653 654
{
    template<class TReal>
655
    inline static TReal getLightness(TReal r, TReal g, TReal b) {
656
        return TReal(0.299)*r + TReal(0.587)*g + TReal(0.114)*b;
657 658 659
    }
    
    template<class TReal>
660 661
    inline static TReal getSaturation(TReal r, TReal g, TReal b) {
        return Arithmetic::max(r,g,b) - Arithmetic::min(r,g,b);
662
    }
663 664 665 666
};

struct HSIType
{
667
    template<class TReal>
668 669
    inline static TReal getLightness(TReal r, TReal g, TReal b) {
        return (r + g + b) * TReal(0.33333333333333333333); // (r + g + b) / 3.0
670 671 672
    }
    
    template<class TReal>
673 674 675 676
    inline static TReal getSaturation(TReal r, TReal g, TReal b) {
        TReal max    = Arithmetic::max(r, g, b);
        TReal min    = Arithmetic::min(r, g, b);
        TReal chroma = max - min;
677
        
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
        return (chroma > std::numeric_limits<TReal>::epsilon()) ?
            (TReal(1.0) - min / getLightness(r, g, b)) : TReal(0.0);
    }
};

struct HSLType
{
    template<class TReal>
    inline static TReal getLightness(TReal r, TReal g, TReal b) {
        TReal max = Arithmetic::max(r, g, b);
        TReal min = Arithmetic::min(r, g, b);
        return (max + min) * TReal(0.5);
    }
    
    template<class TReal>
    inline static TReal getSaturation(TReal r, TReal g, TReal b) {
        TReal max    = Arithmetic::max(r, g, b);
        TReal min    = Arithmetic::min(r, g, b);
        TReal chroma = max - min;
        TReal light  = (max + min) * TReal(0.5);
        TReal div    = TReal(1.0) - std::abs(TReal(2.0)*light - TReal(1.0));
699
        
700 701
        if(div > std::numeric_limits<TReal>::epsilon())
            return chroma / div;
702
        
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
        return TReal(1.0);
    }
};

struct HSVType
{
    template<class TReal>
    inline static TReal getLightness(TReal r, TReal g, TReal b) {
        return Arithmetic::max(r,g,b);
    }
    
    template<class TReal>
    inline static TReal getSaturation(TReal r, TReal g, TReal b) {
        TReal max = Arithmetic::max(r, g, b);
        TReal min = Arithmetic::min(r, g, b);
        return (max == TReal(0.0)) ? TReal(0.0) : (max - min) / max;
    }
};

template<class TReal>
TReal getHue(TReal r, TReal g, TReal b) {
    TReal min    = Arithmetic::min(r, g, b);
    TReal max    = Arithmetic::max(r, g, b);
    TReal chroma = max - min;
    
728
    TReal hue = TReal(-1.0);
729 730
    
    if(chroma > std::numeric_limits<TReal>::epsilon()) {
731 732 733
        
//         return atan2(TReal(2.0)*r - g - b, TReal(1.73205080756887729353)*(g - b));
        
734 735 736 737 738 739
        if(max == r) // between yellow and magenta
            hue = (g - b) / chroma;
        else if(max == g) // between cyan and yellow
            hue = TReal(2.0) + (b - r) / chroma;
        else if(max == b) // between magenta and cyan
            hue = TReal(4.0) + (r - g) / chroma;
740 741 742 743 744
        
        if(hue < -std::numeric_limits<TReal>::epsilon())
            hue += TReal(6.0);
        
        hue /= TReal(6.0);
745 746
    }
    
747 748
//     hue = (r == max) ? (b-g) : (g == max) ? TReal(2.0)+(r-b) : TReal(4.0)+(g-r);
    
749 750 751
    return hue;
}

752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
template<class TReal>
void getRGB(TReal& r, TReal& g, TReal& b, TReal hue) {
    // 0 red    -> (1,0,0)
    // 1 yellow -> (1,1,0)
    // 2 green  -> (0,1,0)
    // 3 cyan   -> (0,1,1)
    // 4 blue   -> (0,0,1)
    // 5 maenta -> (1,0,1)
    // 6 red    -> (1,0,0)
    
    if(hue < -std::numeric_limits<TReal>::epsilon()) {
        r = g = b = TReal(0.0);
        return;
    }
    
    int   i = int(hue * TReal(6.0));
    TReal x = hue * TReal(6.0) - i;
    TReal y = TReal(1.0) - x;
    
    switch(i % 6){
        case 0: { r=TReal(1.0), g=x         , b=TReal(0.0); } break;
        case 1: { r=y         , g=TReal(1.0), b=TReal(0.0); } break;
        case 2: { r=TReal(0.0), g=TReal(1.0), b=x         ; } break;
        case 3: { r=TReal(0.0), g=y         , b=TReal(1.0); } break;
        case 4: { r=x         , g=TReal(0.0), b=TReal(1.0); } break;
        case 5: { r=TReal(1.0), g=TReal(0.0), b=y         ; } break;
    }
}

781 782 783 784 785
template<class HSXType, class TReal>
inline static TReal getLightness(TReal r, TReal g, TReal b) {
    return HSXType::getLightness(r, g, b);
}

786 787 788 789 790 791 792 793 794 795 796 797 798 799
template<class HSXType, class TReal>
inline void addLightness(TReal& r, TReal& g, TReal& b, TReal light)
{
    using namespace Arithmetic;
    
    r += light;
    g += light;
    b += light;
    
    TReal l = HSXType::getLightness(r, g, b);
    TReal n = min(r, g, b);
    TReal x = max(r, g, b);
    
    if(n < TReal(0.0)) {
800 801 802 803
        TReal iln = TReal(1.0) / (l-n);
        r = l + ((r-l) * l) * iln;
        g = l + ((g-l) * l) * iln;
        b = l + ((b-l) * l) * iln;
804 805
    }
    
806 807 808 809 810 811
    if(x > TReal(1.0) && (x-l) > std::numeric_limits<TReal>::epsilon()) {
        TReal il  = TReal(1.0) - l;
        TReal ixl = TReal(1.0) / (x - l);
        r = l + ((r-l) * il) * ixl;
        g = l + ((g-l) * il) * ixl;
        b = l + ((b-l) * il) * ixl;
812 813 814
    }
}

815 816 817 818 819 820
template<class HSXType, class TReal>
inline void setLightness(TReal& r, TReal& g, TReal& b, TReal light)
{
    addLightness<HSXType>(r,g,b, light - HSXType::getLightness(r,g,b));
}

821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
template<class HSXType, class TReal>
inline static TReal getSaturation(TReal r, TReal g, TReal b) {
    return HSXType::getSaturation(r, g, b);
}

template<class HSXType, class TReal>
inline void setSaturation(TReal& r, TReal& g, TReal& b, TReal sat)
{
    int   min    = 0;
    int   mid    = 1;
    int   max    = 2;
    TReal rgb[3] = {r, g, b};
    
    if(rgb[mid] < rgb[min]) {
        int tmp = min;
        min = mid;
        mid = tmp;
    }
    
    if(rgb[max] < rgb[mid]) {
        int tmp = mid;
        mid = max;
        max = tmp;
    }
    
    if(rgb[mid] < rgb[min]) {
        int tmp = min;
        min = mid;
        mid = tmp;
    }
    
    if((rgb[max] - rgb[min]) > TReal(0.0)) {
        rgb[mid] = ((rgb[mid]-rgb[min]) * sat) / (rgb[max]-rgb[min]);
        rgb[max] = sat;
        rgb[min] = TReal(0.0);
856
        
857 858 859
        r = rgb[0];
        g = rgb[1];
        b = rgb[2];
860
    }
861
    else r = g = b = TReal(0.0);
862 863
}

864
#endif