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