Krita Source Code Documentation
Loading...
Searching...
No Matches
KoColorSpaceMaths.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006, 2007, 2010 Cyrille Berger <cberger@cberger.bet
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#ifndef KOCOLORSPACEMATHS_H_
8#define KOCOLORSPACEMATHS_H_
9
10#include <cmath>
11#include <limits>
12
13#include "kritapigment_export.h"
14#include <KoIntegerMaths.h>
15#include "KoChannelInfo.h"
16#include "KoLut.h"
17
18#include <kis_global.h>
19
20#undef _T
21
38template<typename _T>
40{
41public:
42};
43
44template<>
45class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<quint8>
46{
47public:
48 typedef qint32 compositetype;
49 typedef qint64 mixtype;
50 static constexpr quint8 zeroValue = 0;
51 static constexpr quint8 unitValue = 0x00FF;
52 static constexpr quint8 halfValue = 0x00FF / 2;
53 static constexpr quint8 max = 0x00FF;
54 static constexpr quint8 min = 0;
55 static constexpr quint8 epsilon = 1;
56 static constexpr qint8 bits = 8;
58};
59
60template<>
61class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<quint16>
62{
63public:
64 typedef qint64 compositetype;
65 typedef qint64 mixtype;
66 static constexpr quint16 zeroValue = 0;
67 static constexpr quint16 unitValue = 0xFFFF;
68 static constexpr quint16 halfValue = 0xFFFF / 2;
69 static constexpr quint16 max = 0xFFFF;
70 static constexpr quint16 min = 0;
71 static constexpr quint16 epsilon = 1;
72 static constexpr qint8 bits = 16;
74};
75
76template<>
77class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<qint16>
78{
79public:
80 typedef qint64 compositetype;
81 typedef qint64 mixtype;
82 static constexpr qint16 zeroValue = 0;
83 static constexpr qint16 unitValue = 32767;
84 static constexpr qint16 halfValue = 32767 / 2;
85 static constexpr qint16 max = 32767;
86 static constexpr qint16 min = -32768;
87 static constexpr qint16 epsilon = 1;
88 static constexpr qint8 bits = 16;
90};
91
92template<>
93class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<quint32>
94{
95public:
96 typedef qint64 compositetype;
97 typedef qint64 mixtype;
98 static constexpr quint32 zeroValue = 0;
99 static constexpr quint32 unitValue = 0xFFFFFFFF;
100 static constexpr quint32 halfValue = 0xFFFFFFFF / 2;
101 static constexpr quint32 max = 0xFFFFFFFF;
102 static constexpr quint32 min = 0;
103 static constexpr quint32 epsilon = 1;
104 static constexpr qint8 bits = 32;
106};
107
108#include <KoConfig.h>
109#ifdef HAVE_OPENEXR
110#include <half.h>
111#include <KisHalfTraits.h>
112
113template<>
114class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<half>
115{
116public:
117 typedef double compositetype;
118 typedef double mixtype;
119 static const half zeroValue;
120 static const half unitValue;
121 static const half halfValue;
122 static const half max;
123 static const half min;
124 static const half epsilon;
125 static constexpr qint8 bits = 16;
126 static const KoChannelInfo::enumChannelValueType channelValueType;
127};
128#endif
129
130template<>
131class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<float>
132{
133public:
134 typedef float compositetype;
135 typedef double mixtype;
136 static const float zeroValue;
137 static const float unitValue;
138 static const float halfValue;
139 static const float max;
140 static const float min;
141 static const float epsilon;
142 static constexpr qint8 bits = 32;
144};
145
146template<>
147class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<double>
148{
149public:
150 typedef double compositetype;
151 typedef double mixtype;
152 static const double zeroValue;
153 static const double unitValue;
154 static const double halfValue;
155 static const double max;
156 static const double min;
157 static const double epsilon;
158 static constexpr qint8 bits = 64;
160};
161
162inline int float2int(float x)
163{
164 // NOTE: we don't use rint() here, because on Windows
165 // it falls back to x87 instructions on Intel CPUs,
166 // which are executed extremely slowly
167 // NOTE2: we do always clamp value to 0...0xff range
168 // before calling this function, so `x` cannot be
169 // negative.
170 return int(x + 0.5f);
171}
172
173inline int float2int(double x)
174{
175 // NOTE: we don't use rint() here, because on Windows
176 // it falls back to x87 instructions on Intel CPUs,
177 // which are executed extremely slowly
178 // NOTE2: we do always clamp value to 0...0xff range
179 // before calling this function, so `x` cannot be
180 // negative.
181 return int(x + 0.5);
182}
183
184template<typename _T_>
186 inline float operator()(_T_ f) const
187 {
188 return f / float(KoColorSpaceMathsTraits<_T_>::max);
189 }
190};
191
192struct KoLuts {
193
194 static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat<quint16>, float, quint16> Uint16ToFloat;
195 static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat<quint8>, float, quint8> Uint8ToFloat;
196};
197
207template < typename _T, typename _Tdst = _T >
209{
211 typedef typename traits::compositetype src_compositetype;
213
214public:
215 inline static _Tdst multiply(_T a, _Tdst b) {
217 }
218
219 inline static _Tdst multiply(_T a, _Tdst b, _Tdst c) {
221 }
222
228 inline static dst_compositetype divide(_T a, _Tdst b) {
230 }
231
232 inline static dst_compositetype modulus(_T a, _Tdst b) {
233 return (dst_compositetype(a) - floor(dst_compositetype(a)/((b != (KoColorSpaceMathsTraits<_T>::zeroValue - traits::epsilon) ? b : KoColorSpaceMathsTraits<_T>::zeroValue) + traits::epsilon))*(b + traits::epsilon));
234 }
235
236 inline static dst_compositetype xor(_T a, _Tdst b) {
237 return (int (a * std::numeric_limits<int>::max() - traits::epsilon) ^ int (b * std::numeric_limits<int>::max() - traits::epsilon));
238 }
239
240 inline static dst_compositetype and(_T a, _Tdst b) {
241 return (int (a * std::numeric_limits<int>::max() - traits::epsilon) & int (b * std::numeric_limits<int>::max() - traits::epsilon));
242 }
243
244 inline static dst_compositetype or(_T a, _Tdst b) {
245 return (int (a * std::numeric_limits<int>::max() - traits::epsilon) | int (b * std::numeric_limits<int>::max() - traits::epsilon));
246 }
247
252 inline static _T invert(_T a) {
253 return traits::unitValue - a;
254 }
255
262 inline static _T blend(_T a, _T b, _T alpha) {
263 src_compositetype c = ((src_compositetype(a) - b) * alpha) / traits::unitValue;
264 return c + b;
265 }
266
273
275 return kisBoundFast<dst_compositetype>(KoColorSpaceMathsTraits<_Tdst>::min, val, KoColorSpaceMathsTraits<_Tdst>::max);
276 }
277
278 inline static _T clampChannelToSDR(_T val) {
279 if constexpr (std::numeric_limits<_T>::is_integer) {
280 return val;
281 } else {
283 }
284 }
285
286 inline static _T clampChannelToSDRBottom(_T val)
287 {
288 if constexpr (std::numeric_limits<_T>::is_integer) {
289 return val;
290 } else {
291 return qMax<_T>(KoColorSpaceMathsTraits<_T>::zeroValue, val);
292 }
293 }
294
298
300 return qMin<dst_compositetype>(val, KoColorSpaceMathsTraits<_Tdst>::unitValue);
301 }
302
304 return qMax<dst_compositetype>(KoColorSpaceMathsTraits<_Tdst>::zeroValue, val);
305 }
306
308 if constexpr (std::numeric_limits<_Tdst>::is_integer) {
310 } else {
311 return a / b;
312 }
313 }
314
316 if constexpr (std::numeric_limits<_Tdst>::is_integer) {
318 } else {
319 return a * b;
320 }
321 }
322
327 inline static _Tdst clampAfterScale(dst_compositetype val) {
328 return qMin<dst_compositetype>(val, KoColorSpaceMathsTraits<_Tdst>::max);
329 }
330
331 inline static _T isUnsafeAsDivisor(_T value) {
332 static_assert(std::numeric_limits<_Tdst>::is_integer);
334 }
335
336 inline static bool isUnitValueFuzzy(_T value) {
337 static_assert(std::numeric_limits<_Tdst>::is_integer);
339 }
340
341 inline static bool isZeroValueFuzzy(_T value) {
342 static_assert(std::numeric_limits<_Tdst>::is_integer);
344 }
345
346 inline static bool isZeroValueClampedFuzzy(_T v) {
347 static_assert(std::numeric_limits<_Tdst>::is_integer);
348 return v <= 0;
349 }
350
351 static inline bool isUnitValueClampedFuzzy(_T v) {
352 static_assert(std::numeric_limits<_Tdst>::is_integer);
354 }
355
356 static inline bool isHalfValueFuzzy(_T v)
357 {
358 static_assert(std::numeric_limits<_Tdst>::is_integer);
360 }
361};
362
363//------------------------------ double specialization ------------------------------//
364template<>
366{
367 double v = a * 255;
368 return float2int(CLAMP(v, 0, 255));
369}
370
371template<>
373{
374 return KoLuts::Uint8ToFloat(a);
375}
376
377template<>
379{
380 double v = a * 0xFFFF;
381 return float2int(CLAMP(v, 0, 0xFFFF));
382}
383
384template<>
386{
387 return KoLuts::Uint16ToFloat(a);
388}
389
390template<>
391inline double KoColorSpaceMaths<double>::clamp(double a)
392{
393 return a;
394}
395
396template<>
398 return value < 1e-6; // negative values are also unsafe!
399}
400
401template<>
405
406template<>
410
411template<>
413{
414 // constant is from qFuzzyIsNull()
415 return d <= 0.000000000001;
416}
417
418template<>
420{
421 // constant is from qFuzzyIsNull()
422 return d > 1.0 - 0.000000000001;
423}
424
425template<>
429
430//------------------------------ float specialization ------------------------------//
431
432template<>
434{
435 return (float)a;
436}
437
438template<>
440{
441 return a;
442}
443
444template<>
446{
447 float v = a * 0xFFFF;
448 return (quint16)float2int(CLAMP(v, 0, 0xFFFF));
449}
450
451template<>
453{
454 return KoLuts::Uint16ToFloat(a);
455}
456
457template<>
459{
460 float v = a * 255;
461 return (quint8)float2int(CLAMP(v, 0, 255));
462}
463
464template<>
466{
467 return KoLuts::Uint8ToFloat(a);
468}
469
470template<>
471inline float KoColorSpaceMaths<float>::blend(float a, float b, float alpha)
472{
473 return (a - b) * alpha + b;
474}
475
476template<>
478{
479 return a;
480}
481
482template<>
484 return value < 1e-6; // negative values are also unsafe!
485}
486
487template<>
491
492template<>
496
497template<>
499{
500 // constant is from qFuzzyIsNull()
501 return f <= 0.00001f;
502}
503
504template<>
506{
507 // constant is from qFuzzyIsNull()
508 return f > 1.0 - 0.00001f;
509}
510
511template<>
515
516//------------------------------ half specialization ------------------------------//
517
518#ifdef HAVE_OPENEXR
519
520template<>
522{
523 return (half)a;
524}
525
526template<>
528{
529 return a;
530}
531
532template<>
534{
535 return a;
536}
537
538template<>
540{
541 return (half) a;
542}
543
544template<>
546{
547 half v = a * 255;
548 return (quint8)(CLAMP(v, 0, 255));
549}
550
551template<>
553{
554 return a *(1.0 / 255.0);
555}
556template<>
558{
559 double v = a * 0xFFFF;
560 return (quint16)(CLAMP(v, 0, 0xFFFF));
561}
562
563template<>
565{
566 return a *(1.0 / 0xFFFF);
567}
568
569template<>
571{
572 return a;
573}
574
575template<>
576inline half KoColorSpaceMaths<half>::blend(half a, half b, half alpha)
577{
578 return (a - b) * alpha + b;
579}
580
581template<>
582inline double KoColorSpaceMaths<half>::clamp(double a)
583{
584 return a;
585}
586
587template<>
589 return value < 1e-6; // negative values are also unsafe!
590}
591
592template<>
594 // ~ 2 * HALF_ESPILON
595 return qAbs(value - KoColorSpaceMathsTraits<half>::unitValue) < 0.002f;
596}
597
598template<>
600 // ~ 2 * HALF_ESPILON
601 return qAbs(value) < 0.002f;
602}
603
604template<>
606{
607 // ~ 2 * HALF_ESPILON
608 return f <= 0.002f;
609}
610
611template<>
613{
614 // ~ 2 * HALF_ESPILON
615 return f > 1.0 - 0.002f;
616}
617
618template<>
620 // TODO: check actual constant
621 return qAbs(value - 0.5f) < 0.001f;
622}
623
624#endif
625
626//------------------------------ quint8 specialization ------------------------------//
627
628template<>
629inline quint8 KoColorSpaceMaths<quint8>::multiply(quint8 a, quint8 b)
630{
631 return (quint8)UINT8_MULT(a, b);
632}
633
634
635template<>
636inline quint8 KoColorSpaceMaths<quint8>::multiply(quint8 a, quint8 b, quint8 c)
637{
638 return (quint8)UINT8_MULT3(a, b, c);
639}
640
641template<>
644{
645 return UINT8_DIVIDE(a, b);
646}
647
648template<>
650{
651 return ~a;
652}
653
654template<>
655inline quint8 KoColorSpaceMaths<quint8>::blend(quint8 a, quint8 b, quint8 c)
656{
657 return UINT8_BLEND(a, b, c);
658}
659
660//------------------------------ quint16 specialization ------------------------------//
661
662template<>
663inline quint16 KoColorSpaceMaths<quint16>::multiply(quint16 a, quint16 b)
664{
665 return (quint16)UINT16_MULT(a, b);
666}
667
668template<>
671{
672 return UINT16_DIVIDE(a, b);
673}
674
675template<>
676inline quint16 KoColorSpaceMaths<quint16>::invert(quint16 a)
677{
678 return ~a;
679}
680
681//------------------------------ various specialization ------------------------------//
682
683
684// TODO: use more functions from KoIntegersMaths to do the computation
685
687template<>
689{
690 return UINT8_TO_UINT16(a);
691}
692
693template<>
695{
696 return UINT16_TO_UINT8(a);
697}
698
699
700// Due to once again a bug in gcc, there is the need for those specialized functions:
701
702template<>
704{
705 return a;
706}
707
708template<>
710{
711 return a;
712}
713
714template<>
716{
717 return a;
718}
719
720namespace Arithmetic
721{
722 const static qreal pi = 3.14159265358979323846;
723
724 template<class T>
725 inline T mul(T a, T b) { return KoColorSpaceMaths<T>::multiply(a, b); }
726
727 template<class T>
728 inline T mul(T a, T b, T c) { return KoColorSpaceMaths<T>::multiply(a, b, c); }
729
730// template<class T>
731// inline T mul(T a, T b) {
732// typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
733// return T(composite_type(a) * b / KoColorSpaceMathsTraits<T>::unitValue);
734// }
735//
736// template<class T>
737// inline T mul(T a, T b, T c) {
738// typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
739// return T((composite_type(a) * b * c) / (composite_type(KoColorSpaceMathsTraits<T>::unitValue) * KoColorSpaceMathsTraits<T>::unitValue));
740// }
741
742 template<class T>
743 inline T inv(T a) { return KoColorSpaceMaths<T>::invert(a); }
744
745 template<class T>
746 inline T lerp(T a, T b, T alpha) { return KoColorSpaceMaths<T>::blend(b, a, alpha); }
747
748 template<class TRet, class T>
749 inline TRet scale(T a) { return KoColorSpaceMaths<T,TRet>::scaleToA(a); }
750
751 template<class T>
753 div(T a, T b) { return KoColorSpaceMaths<T>::divide(a, b); }
754
755 template<class T>
757 xor(T a, T b) { return KoColorSpaceMaths<T>::xor(a, b); }
758
759 template<class T>
761 and(T a, T b) { return KoColorSpaceMaths<T>::and(a, b); }
762
763 template<class T>
765 or(T a, T b) { return KoColorSpaceMaths<T>::or(a, b); }
766
767 template<class T>
771
772 template<class T>
773 inline T clampChannelToSDR(T a) {
775 }
776
777 template<class T>
781
782 template<class T>
786
787 template<class T>
791
792 template<class T>
796
797 template<class T, typename composite_type = typename KoColorSpaceMathsTraits<T>::compositetype>
798 inline composite_type divideInCompositeSpace(composite_type a, composite_type b) {
800 }
801
802 template<class T, typename composite_type = typename KoColorSpaceMathsTraits<T>::compositetype>
803 inline composite_type multiplyInCompositeSpace(composite_type a, composite_type b) {
805 }
806
807 template <typename T>
808 static inline bool isZeroValueFuzzy(T v)
809 {
811 }
812
813 template <typename T>
814 static inline bool isUnitValueFuzzy(T v)
815 {
817 }
818
819 template <typename T>
820 static inline bool isZeroValueClampedFuzzy(T v)
821 {
823 }
824
825 template <typename T>
826 static inline bool isUnitValueClampedFuzzy(T v)
827 {
829 }
830
831 template <typename T>
832 static inline bool isHalfValueFuzzy(T v)
833 {
835 }
836
837 template <typename T>
841 template <typename T>
845
846 template <typename T>
848 if constexpr (std::numeric_limits<T>::is_integer) {
850 } else {
852 }
853 }
854 template <typename T>
856 if constexpr (std::numeric_limits<T>::is_integer) {
858 } else {
859 return value <= KoColorSpaceMathsTraits<T>::zeroValue;
860 }
861 }
862
863
864 template<class T>
865 inline T min(T a, T b, T c) {
866 b = (a < b) ? a : b;
867 return (b < c) ? b : c;
868 }
869
870 template<class T>
871 inline T max(T a, T b, T c) {
872 b = (a > b) ? a : b;
873 return (b > c) ? b : c;
874 }
875
876 template<class T>
878
879 template<class T>
881
882 template<class T>
884
885 template<class T>
886 inline T unionShapeOpacity(T a, T b) {
887 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
888 return T(composite_type(a) + b - mul(a,b));
889 }
890
891 template<class T>
892 inline T blend(T src, T srcAlpha, T dst, T dstAlpha, T cfValue) {
893 return mul(inv(srcAlpha), dstAlpha, dst) + mul(inv(dstAlpha), srcAlpha, src) + mul(dstAlpha, srcAlpha, cfValue);
894 }
895
896 template<class T>
898
899 template<class T>
901 mod(T a, T b) { return KoColorSpaceMaths<T>::modulus(a, b); }
902
903 template<typename T>
907}
908
910{
911 template<class TReal>
912 inline static TReal getLightness(TReal r, TReal g, TReal b) {
913 return TReal(0.299)*r + TReal(0.587)*g + TReal(0.114)*b;
914 }
915
916 template<class TReal>
917 inline static TReal getSaturation(TReal r, TReal g, TReal b) {
918 return Arithmetic::max(r,g,b) - Arithmetic::min(r,g,b);
919 }
920
921 static constexpr bool lightnessIsAverage = true;
922};
923
925{
926 template<class TReal>
927 inline static TReal getLightness(TReal r, TReal g, TReal b) {
928 return (r + g + b) * TReal(0.33333333333333333333); // (r + g + b) / 3.0
929 }
930
931 template<class TReal>
932 inline static TReal getSaturation(TReal r, TReal g, TReal b) {
933 TReal max = Arithmetic::max(r, g, b);
934 TReal min = Arithmetic::min(r, g, b);
935 TReal chroma = max - min;
936
937 return (chroma > std::numeric_limits<TReal>::epsilon()) ?
938 (TReal(1.0) - min / getLightness(r, g, b)) : TReal(0.0);
939 }
940
941 static constexpr bool lightnessIsAverage = true;
942};
943
945{
946 template<class TReal>
947 inline static TReal getLightness(TReal r, TReal g, TReal b) {
948 TReal max = Arithmetic::max(r, g, b);
949 TReal min = Arithmetic::min(r, g, b);
950 return (max + min) * TReal(0.5);
951 }
952
953 template<class TReal>
954 inline static TReal getSaturation(TReal r, TReal g, TReal b) {
955 TReal max = Arithmetic::max(r, g, b);
956 TReal min = Arithmetic::min(r, g, b);
957 TReal chroma = max - min;
958 TReal light = (max + min) * TReal(0.5);
959 TReal div = TReal(1.0) - std::abs(TReal(2.0)*light - TReal(1.0));
960
961 if(div > std::numeric_limits<TReal>::epsilon())
962 return chroma / div;
963
964 return TReal(0.0);
965 }
966
967 static constexpr bool lightnessIsAverage = true;
968};
969
971{
972 template<class TReal>
973 inline static TReal getLightness(TReal r, TReal g, TReal b) {
974 return Arithmetic::max(r,g,b);
975 }
976
977 template<class TReal>
978 inline static TReal getSaturation(TReal r, TReal g, TReal b) {
979 TReal max = Arithmetic::max(r, g, b);
980 TReal min = Arithmetic::min(r, g, b);
981 return (max > std::numeric_limits<TReal>::epsilon()) ? (max - min) / max : TReal(0.0);
982 }
983
984 static constexpr bool lightnessIsAverage = false;
985};
986
987template<class TReal>
988TReal getHue(TReal r, TReal g, TReal b) {
989 TReal min = Arithmetic::min(r, g, b);
990 TReal max = Arithmetic::max(r, g, b);
991 TReal chroma = max - min;
992
993 TReal hue = TReal(-1.0);
994
995 if(chroma > std::numeric_limits<TReal>::epsilon()) {
996
997// return atan2(TReal(2.0)*r - g - b, TReal(1.73205080756887729353)*(g - b));
998
999 if(max == r) // between yellow and magenta
1000 hue = (g - b) / chroma;
1001 else if(max == g) // between cyan and yellow
1002 hue = TReal(2.0) + (b - r) / chroma;
1003 else if(max == b) // between magenta and cyan
1004 hue = TReal(4.0) + (r - g) / chroma;
1005
1006 if(hue < -std::numeric_limits<TReal>::epsilon())
1007 hue += TReal(6.0);
1008
1009 hue /= TReal(6.0);
1010 }
1011
1012// hue = (r == max) ? (b-g) : (g == max) ? TReal(2.0)+(r-b) : TReal(4.0)+(g-r);
1013
1014 return hue;
1015}
1016
1017template<class TReal>
1018void getRGB(TReal& r, TReal& g, TReal& b, TReal hue) {
1019 // 0 red -> (1,0,0)
1020 // 1 yellow -> (1,1,0)
1021 // 2 green -> (0,1,0)
1022 // 3 cyan -> (0,1,1)
1023 // 4 blue -> (0,0,1)
1024 // 5 magenta -> (1,0,1)
1025 // 6 red -> (1,0,0)
1026
1027 if(hue < -std::numeric_limits<TReal>::epsilon()) {
1028 r = g = b = TReal(0.0);
1029 return;
1030 }
1031
1032 int i = int(hue * TReal(6.0));
1033 TReal x = hue * TReal(6.0) - i;
1034 TReal y = TReal(1.0) - x;
1035
1036 switch(i % 6){
1037 case 0: { r=TReal(1.0), g=x , b=TReal(0.0); } break;
1038 case 1: { r=y , g=TReal(1.0), b=TReal(0.0); } break;
1039 case 2: { r=TReal(0.0), g=TReal(1.0), b=x ; } break;
1040 case 3: { r=TReal(0.0), g=y , b=TReal(1.0); } break;
1041 case 4: { r=x , g=TReal(0.0), b=TReal(1.0); } break;
1042 case 5: { r=TReal(1.0), g=TReal(0.0), b=y ; } break;
1043 }
1044}
1045
1046template<class HSXType, class TReal>
1047inline static TReal getLightness(TReal r, TReal g, TReal b) {
1048 return HSXType::getLightness(r, g, b);
1049}
1050
1051template<class HSXType, class TReal>
1052inline void ToneMapping(TReal& r, TReal& g, TReal& b)
1053{
1054 using namespace Arithmetic;
1055
1056
1057 TReal l = HSXType::getLightness(r, g, b);
1058 TReal n = min(r, g, b);
1059 TReal x = max(r, g, b);
1060
1061 if(n < TReal(0.0)) {
1062 if (isZeroValueClampedFuzzy<float>(l)) {
1070 r = g = b = TReal(0.0);
1071 } else {
1072 const TReal stretch = l - n;
1073
1074 if (stretch < std::numeric_limits<TReal>::epsilon()) {
1075 r = g = b = TReal(0.0);
1076 } else {
1077 TReal iln = TReal(1.0) / stretch;
1078 r = l + ((r-l) * l) * iln;
1079 g = l + ((g-l) * l) * iln;
1080 b = l + ((b-l) * l) * iln;
1081 }
1082 }
1083 }
1084
1085 if(x > TReal(1.0)) {
1086 auto setFallbackValues = [&] () {
1087 if constexpr (HSXType::lightnessIsAverage) {
1088 r = g = b = TReal(1.0);
1089 } else {
1090 r = qMin(r, TReal(1.0));
1091 g = qMin(g, TReal(1.0));
1092 b = qMin(b, TReal(1.0));
1093 }
1094 };
1095
1096 if (l > TReal(1.0)) {
1104 setFallbackValues();
1105 } else {
1106 const TReal stretch = x - l;
1107
1108 if (stretch < std::numeric_limits<TReal>::epsilon()) {
1109 setFallbackValues();
1110 } else {
1111 TReal il = TReal(1.0) - l;
1112 TReal ixl = TReal(1.0) / stretch;
1113
1114 r = l + ((r-l) * il) * ixl;
1115 g = l + ((g-l) * il) * ixl;
1116 b = l + ((b-l) * il) * ixl;
1117 }
1118 }
1119 }
1120}
1121
1122template<class HSXType, class TReal>
1123inline void addLightness(TReal& r, TReal& g, TReal& b, TReal light)
1124{
1125 using namespace Arithmetic;
1126
1127 r += light;
1128 g += light;
1129 b += light;
1130
1131 ToneMapping<HSXType, TReal>(r, g, b);
1132}
1133
1134template<class HSXType, class TReal>
1135inline void setLightness(TReal& r, TReal& g, TReal& b, TReal light)
1136{
1137 addLightness<HSXType>(r,g,b, light - HSXType::getLightness(r,g,b));
1138}
1139
1140template<class HSXType, class TReal>
1141inline static TReal getSaturation(TReal r, TReal g, TReal b) {
1142 return HSXType::getSaturation(r, g, b);
1143}
1144
1145template<class HSXType, class TReal>
1146inline void setSaturation(TReal& r, TReal& g, TReal& b, TReal sat)
1147{
1148 int min = 0;
1149 int mid = 1;
1150 int max = 2;
1151 TReal rgb[3] = {r, g, b};
1152
1153 if(rgb[mid] < rgb[min]) {
1154 int tmp = min;
1155 min = mid;
1156 mid = tmp;
1157 }
1158
1159 if(rgb[max] < rgb[mid]) {
1160 int tmp = mid;
1161 mid = max;
1162 max = tmp;
1163 }
1164
1165 if(rgb[mid] < rgb[min]) {
1166 int tmp = min;
1167 min = mid;
1168 mid = tmp;
1169 }
1170
1171 if((rgb[max] - rgb[min]) > std::numeric_limits<TReal>::epsilon()) {
1172 rgb[mid] = ((rgb[mid]-rgb[min]) * sat) / (rgb[max]-rgb[min]);
1173 rgb[max] = sat;
1174 rgb[min] = TReal(0.0);
1175
1176 r = rgb[0];
1177 g = rgb[1];
1178 b = rgb[2];
1179 }
1180 else r = g = b = TReal(0.0);
1181}
1182
1183#endif
float value(const T *src, size_t ch)
qreal v
static TReal getLightness(TReal r, TReal g, TReal b)
void setLightness(TReal &r, TReal &g, TReal &b, TReal light)
void getRGB(TReal &r, TReal &g, TReal &b, TReal hue)
TReal getHue(TReal r, TReal g, TReal b)
void setSaturation(TReal &r, TReal &g, TReal &b, TReal sat)
static TReal getSaturation(TReal r, TReal g, TReal b)
void addLightness(TReal &r, TReal &g, TReal &b, TReal light)
void ToneMapping(TReal &r, TReal &g, TReal &b)
int float2int(float x)
uint UINT8_MULT(uint a, uint b)
uint UINT8_BLEND(uint a, uint b, uint alpha)
uint UINT16_MULT(uint a, uint b)
uint UINT8_TO_UINT16(uint c)
uint UINT16_DIVIDE(uint a, uint b)
uint UINT16_TO_UINT8(uint c)
uint UINT8_DIVIDE(uint a, uint b)
uint UINT8_MULT3(uint a, uint b, uint c)
Approximation of (a * b * c + 32512) / 65025.0.
enumChannelValueType
enum to define the value of the channel
static const KoChannelInfo::enumChannelValueType channelValueType
static const KoChannelInfo::enumChannelValueType channelValueType
static const KoChannelInfo::enumChannelValueType channelValueType
static const KoChannelInfo::enumChannelValueType channelValueType
static const KoChannelInfo::enumChannelValueType channelValueType
static const KoChannelInfo::enumChannelValueType channelValueType
KoColorSpaceMathsTraits< _Tdst >::compositetype dst_compositetype
static _T blend(_T a, _T b, _T alpha)
static dst_compositetype xor(_T a, _Tdst b)
static dst_compositetype clamp(dst_compositetype val)
static dst_compositetype clampToSDR(dst_compositetype val)
static dst_compositetype or(_T a, _Tdst b)
static dst_compositetype divideInCompositeSpace(dst_compositetype a, dst_compositetype b)
static _T isUnsafeAsDivisor(_T value)
static dst_compositetype clampToSDRBottom(dst_compositetype val)
static bool isUnitValueFuzzy(_T value)
static dst_compositetype multiplyInCompositeSpace(dst_compositetype a, dst_compositetype b)
static _T clampChannelToSDR(_T val)
static bool isZeroValueFuzzy(_T value)
static dst_compositetype and(_T a, _Tdst b)
static _Tdst multiply(_T a, _Tdst b)
static _T clampChannelToSDRBottom(_T val)
static _Tdst clampAfterScale(dst_compositetype val)
static bool isUnitValueClampedFuzzy(_T v)
static dst_compositetype clampToSDRTop(dst_compositetype val)
static bool isHalfValueFuzzy(_T v)
static _T invert(_T a)
static _Tdst multiply(_T a, _Tdst b, _Tdst c)
static _Tdst scaleToA(_T a)
traits::compositetype src_compositetype
static bool isZeroValueClampedFuzzy(_T v)
KoColorSpaceMathsTraits< _T > traits
static dst_compositetype modulus(_T a, _Tdst b)
static dst_compositetype divide(_T a, _Tdst b)
static bool qFuzzyCompare(half p1, half p2)
static bool qFuzzyIsNull(half h)
#define CLAMP(x, l, h)
bool isUnitValueStrict(T value)
bool isUnitValueClampedStrict(T value)
static bool isHalfValueFuzzy(T v)
T mul(T a, T b)
T clamp(typename KoColorSpaceMathsTraits< T >::compositetype a)
static bool isUnitValueClampedFuzzy(T v)
T max(T a, T b, T c)
composite_type multiplyInCompositeSpace(composite_type a, composite_type b)
T blend(T src, T srcAlpha, T dst, T dstAlpha, T cfValue)
T lerp(T a, T b, T alpha)
T clampChannelToSDR(T a)
bool isZeroValueStrict(T value)
T clampChannelToSDRBottom(T a)
bool isZeroValueClampedStrict(T value)
T clampToSDRBottom(typename KoColorSpaceMathsTraits< T >::compositetype a)
T clampToSDR(typename KoColorSpaceMathsTraits< T >::compositetype a)
T isUnsafeAsDivisor(T value)
T unionShapeOpacity(T a, T b)
KoColorSpaceMathsTraits< T >::compositetype xor(T a, T b)
composite_type divideInCompositeSpace(composite_type a, composite_type b)
static const qreal pi
T min(T a, T b, T c)
KoColorSpaceMathsTraits< T >::compositetype div(T a, T b)
static bool isZeroValueClampedFuzzy(T v)
KoColorSpaceMathsTraits< T >::compositetype or(T a, T b)
KoColorSpaceMathsTraits< T >::compositetype mod(T a, T b)
KoColorSpaceMathsTraits< T >::compositetype and(T a, T b)
static bool isZeroValueFuzzy(T v)
static bool isUnitValueFuzzy(T v)
T clampToSDRTop(typename KoColorSpaceMathsTraits< T >::compositetype a)
static TReal getSaturation(TReal r, TReal g, TReal b)
static constexpr bool lightnessIsAverage
static TReal getLightness(TReal r, TReal g, TReal b)
static TReal getLightness(TReal r, TReal g, TReal b)
static TReal getSaturation(TReal r, TReal g, TReal b)
static constexpr bool lightnessIsAverage
static TReal getSaturation(TReal r, TReal g, TReal b)
static constexpr bool lightnessIsAverage
static TReal getLightness(TReal r, TReal g, TReal b)
static TReal getSaturation(TReal r, TReal g, TReal b)
static TReal getLightness(TReal r, TReal g, TReal b)
static constexpr bool lightnessIsAverage
float operator()(_T_ f) const
static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat< quint16 >, float, quint16 > Uint16ToFloat
static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat< quint8 >, float, quint8 > Uint8ToFloat