Krita Source Code Documentation
Loading...
Searching...
No Matches
KoCompositeOpFunctions.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#ifndef KOCOMPOSITEOP_FUNCTIONS_H_
8#define KOCOMPOSITEOP_FUNCTIONS_H_
9
10#include <KoColorSpaceMaths.h>
13
14#include <type_traits>
15#include <cmath>
16
17#ifdef HAVE_OPENEXR
18#include "half.h"
19#endif
20
21namespace {
22template<typename channels_type>
23inline void possiblyFixNegativeValuesNearZeroPoint(float& dstR, float& dstG, float& dstB)
24{
30 if constexpr (!std::numeric_limits<channels_type>::is_integer) {
31 dstR = qMax(dstR, 0.0f);
32 dstG = qMax(dstG, 0.0f);
33 dstB = qMax(dstB, 0.0f);
34 }
35}
36
37template<typename channels_type>
38inline void possiblyClampValuesToSDR(float& dstR, float& dstG, float& dstB)
39{
45 if constexpr (!std::numeric_limits<channels_type>::is_integer) {
46 dstR = qBound(0.0f, dstR, 1.0f);
47 dstG = qBound(0.0f, dstG, 1.0f);
48 dstB = qBound(0.0f, dstB, 1.0f);
49 }
50}
51}
52
53template<typename channels_type>
55{
56 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
57 // see https://blog.selfshadow.com/publications/blending-in-detail/ by Barre-Brisebois and Hill
58
59 using namespace Arithmetic;
60
66 if (isZeroValueFuzzy(srcB)) {
67 return;
68 }
69
75 float tx = 2*srcR-1;
76 float ty = 2*srcG-1;
77 float tz = 2*srcB;
78 float ux = -2*dstR+1;
79 float uy = -2*dstG+1;
80 float uz = 2*dstB-1;
81 float k = (tx*ux+ty*uy+tz*uz)/tz; // dot(t,u)/t.z
82 float rx = tx*k-ux;
83 float ry = ty*k-uy;
84 float rz = tz*k-uz;
85
86 float finalLength = rx*rx+ry*ry+rz*rz;
87
88
94 if (isZeroValueStrict(finalLength)) {
95 return;
96 }
97
98 k = 1 / sqrt(finalLength); // normalize result
99 rx *= k;
100 ry *= k;
101 rz *= k;
102 dstR = rx*0.5+0.5;
103 dstG = ry*0.5+0.5;
104 dstB = rz*0.5+0.5;
105 }
106};
107
108template<class HSXType, typename channels_type>
110{
111 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
112 using namespace Arithmetic;
113 float lum = getLightness<HSXType>(dstR, dstG, dstB);
114 dstR = srcR;
115 dstG = srcG;
116 dstB = srcB;
117 setLightness<HSXType>(dstR, dstG, dstB, lum);
118 possiblyFixNegativeValuesNearZeroPoint<channels_type>(dstR, dstG, dstB);
119 }
120};
121
122template<class HSXType, typename channels_type>
124{
125 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
126 using namespace Arithmetic;
127 // This mode is useful to change the color of a sketch, to make it easier to ink over it.
128 // Unlike the Color function above, it changes black to the src color and interpolates to white.
129
130 const float light = getLightness<HSXType>(dstR, dstG, dstB);
131 const float oneMinusLight = unitValue<float>() - light;
132 dstR = light + srcR * oneMinusLight;
133 dstG = light + srcG * oneMinusLight;
134 dstB = light + srcB * oneMinusLight;
135 }
136};
137
138template<class HSXType, typename channels_type>
140{
141 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
142 float tr = srcR * dstR * (1.0 / 0.215686);
143 float tg = srcG * dstG * (1.0 / 0.215686);
144 float tb = srcB * dstB * (1.0 / 0.215686);
145
146 if (tr > 1.0) {
147 dstR = 1.0 + (tr - 1.0) * (tr - 1.0) * 0.01925;
148 }
149 else {
150 dstR = tr;
151 }
152
153 if (tg > 1.0) {
154 dstG = 1.0 + (tg - 1.0) * (tg - 1.0) * 0.01925;
155 }
156 else {
157 dstG = tg;
158 }
159
160 if (tb > 1.0) {
161 dstB = 1.0 + (tb - 1.0) * (tb - 1.0) * 0.01925;
162 }
163 else {
164 dstB = tb;
165 }
166
167 ToneMapping<HSXType, float>(dstR, dstG, dstB);
168 }
169};
170
171template<class HSXType, typename channels_type>
173{
174 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
175 float tr = srcR * dstR * 2.0;
176 float tg = srcG * dstG * 2.0;
177 float tb = srcB * dstB * 2.0;
178
179 if (tr > 1.0) {
180 dstR = 1.0 + (tr - 1.0) * (tr - 1.0) * 0.4;
181 }
182 else {
183 dstR = tr;
184 }
185
186 if (tg > 1.0) {
187 dstG = 1.0 + (tg - 1.0) * (tg - 1.0) * 0.4;
188 }
189 else {
190 dstG = tg;
191 }
192
193 if (tb > 1.0) {
194 dstB = 1.0 + (tb - 1.0) * (tb - 1.0) * 0.4;
195 }
196 else {
197 dstB = tb;
198 }
199
200 ToneMapping<HSXType, float>(dstR, dstG, dstB);
201 }
202};
203
204template<class HSXType, typename channels_type>
206{
207 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
208 setLightness<HSXType>(dstR, dstG, dstB, getLightness<HSXType>(srcR, srcG, srcB));
209 possiblyFixNegativeValuesNearZeroPoint<channels_type>(dstR, dstG, dstB);
210 }
211};
212
213
214template<class HSXType, typename channels_type>
216{
217 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
218 addLightness<HSXType>(dstR, dstG, dstB, getLightness<HSXType>(srcR, srcG, srcB));
219 possiblyFixNegativeValuesNearZeroPoint<channels_type>(dstR, dstG, dstB);
220 }
221};
222
223template<class HSXType, typename channels_type>
225{
226 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
227 addLightness<HSXType>(dstR, dstG, dstB, getLightness<HSXType>(srcR, srcG, srcB) - 1.0f);
228 possiblyFixNegativeValuesNearZeroPoint<channels_type>(dstR, dstG, dstB);
229 }
230};
231
232template<class HSXType, typename channels_type>
234{
235 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
236 float sat = getSaturation<HSXType>(srcR, srcG, srcB);
237 float light = getLightness<HSXType>(dstR, dstG, dstB);
238 setSaturation<HSXType>(dstR, dstG, dstB, sat);
239 setLightness<HSXType>(dstR, dstG, dstB, light);
240 possiblyFixNegativeValuesNearZeroPoint<channels_type>(dstR, dstG, dstB);
241 }
242};
243
244template<class HSXType, typename channels_type>
246{
247 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
248 using namespace Arithmetic;
249 float sat = lerp(getSaturation<HSXType>(dstR, dstG, dstB), unitValue<float>(), getSaturation<HSXType>(srcR, srcG, srcB));
250 float light = getLightness<HSXType>(dstR, dstG, dstB);
251 setSaturation<HSXType>(dstR, dstG, dstB, sat);
252 setLightness<HSXType>(dstR, dstG, dstB, light);
253 possiblyFixNegativeValuesNearZeroPoint<channels_type>(dstR, dstG, dstB);
254 }
255};
256
257template<class HSXType, typename channels_type>
259{
260 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
261 using namespace Arithmetic;
262 float sat = lerp(zeroValue<float>(), getSaturation<HSXType>(dstR, dstG, dstB), getSaturation<HSXType>(srcR, srcG, srcB));
263 float light = getLightness<HSXType>(dstR, dstG, dstB);
264 setSaturation<HSXType>(dstR, dstG, dstB, sat);
265 setLightness<HSXType>(dstR, dstG, dstB, light);
266 possiblyFixNegativeValuesNearZeroPoint<channels_type>(dstR, dstG, dstB);
267 }
268};
269
270template<class HSXType, typename channels_type>
272{
273 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
274 float sat = getSaturation<HSXType>(dstR, dstG, dstB);
275 float lum = getLightness<HSXType>(dstR, dstG, dstB);
276
277 dstR = srcR;
278 dstG = srcG;
279 dstB = srcB;
280
281 setSaturation<HSXType>(dstR, dstG, dstB, sat);
282 setLightness<HSXType>(dstR, dstG, dstB, lum);
283 possiblyFixNegativeValuesNearZeroPoint<channels_type>(dstR, dstG, dstB);
284 }
285};
286
287template<typename channels_type>
289{
290 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
291 using namespace Arithmetic;
292 const float half = halfValue<float>();
293 dstR = srcR + (dstR - half);
294 dstG = srcG + (dstG - half);
295 dstB = srcB + (dstB - unitValue<float>());
296 possiblyClampValuesToSDR<channels_type>(dstR, dstG, dstB);
297 }
298};
299
300template<class HSXType, typename channels_type>
302{
303 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
304 const float lum = getLightness<HSXType>(dstR, dstG, dstB);
305 const float lum2 = getLightness<HSXType>(srcR, srcG, srcB);
306 if (lum > lum2) {
307 dstR = srcR;
308 dstG = srcG;
309 dstB = srcB;
310 }
311 }
312};
313
314template<class HSXType, typename channels_type>
316{
317 static void composeChannels(float srcR, float srcG, float srcB, float& dstR, float& dstG, float& dstB) {
318 const float lum = getLightness<HSXType>(dstR, dstG, dstB);
319 const float lum2 = getLightness<HSXType>(srcR, srcG, srcB);
320 if (lum < lum2) {
321 dstR = srcR;
322 dstG = srcG;
323 dstB = srcB;
324 }
325 }
326};
327
328template<class T>
330{
331 static T composeChannel(T src, T dst) {
332 using namespace Arithmetic;
333
338 if (isUnitValueStrict(dst)) {
339 return unitValue<T>();
340 }
341
342 if constexpr (std::numeric_limits<T>::is_integer) {
343 // Handle the case where the denominator is 0. See color dodge for a
344 // detailed explanation
345 if (isZeroValueStrict(src)) {
346 return zeroValue<T>();
347 }
348 }
349
350 using composite_type = typename KoColorSpaceMathsTraits<T>::compositetype;
351 composite_type divisionResult = div(inv(dst), src);
352
353 if constexpr (!std::numeric_limits<T>::is_integer) {
354 if (!std::isfinite(divisionResult)) {
355 return zeroValue<T>();
356 }
357 }
358
359 return inv<T>(clampToSDR<T>(divisionResult));
360 }
361};
362
363
364template<class T,
365 template <typename> typename ClampPolicy>
367 static inline T composeChannel(T src, T dst) {
368 using namespace Arithmetic;
369 using clamp_policy = ClampPolicy<T>;
370 using composite_type = typename KoColorSpaceMathsTraits<T>::compositetype;
371
372 return clamp_policy::clampResult(composite_type(src) + dst - unitValue<T>());
373 }
374};
375
376template<typename T,
377 template <typename> typename ClampPolicy>
379 static inline T composeChannel(T src, T dst) {
380 using namespace Arithmetic;
381 using clamp_policy = ClampPolicy<T>;
382
383 // Handle the case where the denominator is 0.
384 // When src is 1 then the denominator (1 - src) becomes 0, and to avoid
385 // dividing by 0 we treat the denominator as an infinitely small number,
386 // so the result of the formula would approach infinity. As in the generic
387 // case, that result is clamped to the maximum value (which for integer
388 // types is the same as the unit value).
389 // Another special case is when both numerator and denominator are 0. In
390 // this case we also treat the denominator as an infinitely small number,
391 // and the numerator can remain as 0, so dividing 0 over a number (no matter
392 // how small it is) gives 0.
393 if (isUnitValueStrict(src)) {
394 return isZeroValueClampedStrict(dst) ? zeroValue<T>() : KoColorSpaceMathsTraits<T>::unitValue;
395 }
396
397 return clamp_policy::clampResultAllowNegative(
398 clamp_policy::fixInfiniteAfterDivision(
399 div(dst, inv(src))
400 )
401 );
402 }
403};
404
405template<class T>
406inline T cfAddition(T src, T dst) {
407 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
408 return Arithmetic::clamp<T>(composite_type(src) + dst);
409}
410
411template<class T>
412inline T cfSubtract(T src, T dst) {
413 using namespace Arithmetic;
414 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
415 return clamp<T>(composite_type(dst) - src);
416}
417
418template<class T>
420 static inline T composeChannel(T src, T dst) {
421 using namespace Arithmetic;
422 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
423 return clamp<T>(composite_type(dst) - inv(src));
424 }
425};
426
427template<class T>
429 static inline T composeChannel(T src, T dst) {
430 using namespace Arithmetic;
431 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
432
433 composite_type x = mul(src, dst);
434 return clamp<T>(composite_type(dst) + src - (x + x));
435 }
436};
437
438
439
440template<class T>
442 static inline T composeChannel(T src, T dst) {
443 using namespace Arithmetic;
444 using composite_type = typename KoColorSpaceMathsTraits<T>::compositetype;
445
446 if (isZeroValueStrict(dst)) {
447 return zeroValue<T>();
448 }
449
450 if (isZeroValueStrict(src)) {
451 return unitValue<T>();
452 }
453
454 composite_type result = div(dst, src);
455
461 return clampToSDRTop<T>(result);
462 }
463};
464
465template<class T>
467 static inline T composeChannel(T src, T dst) {
468 using namespace Arithmetic;
469 using composite_type = typename KoColorSpaceMathsTraits<T>::compositetype;
470
471 if (isHalfValueFuzzy(src)) {
472 return dst;
473 }
474
475 composite_type src2 = composite_type(src) + src;
476
477 if(src > halfValue<T>()) {
478 // screen(src*2.0 - 1.0, dst)
479 src2 -= unitValue<T>();
480
481 // src2 is guaranteed to be smaller than unitValue<T>() now
482 return Arithmetic::unionShapeOpacity(T(src2), dst);
483 }
484
485 // src2 is guaranteed to be smaller than unitValue<T>() due to 'if'
486 return Arithmetic::mul(T(src2), dst);
487 }
488};
489
490template<class T>
492 static inline T composeChannel(T src, T dst) {
493 using namespace Arithmetic;
494
500 qreal fsrc = scale<qreal>(src);
501 qreal fdst = scale<qreal>(dst);
502
503 if(fsrc > 0.5) {
504 qreal D = (fdst > 0.25) ? sqrt(fdst) : ((16.0*fdst - 12.0)*fdst + 4.0)*fdst;
505 return scale<T>(fdst + (2.0*fsrc - 1.0) * (D - fdst));
506 }
507
508 return scale<T>(fdst - (1.0 - 2.0 * fsrc) * fdst * (1.0 - fdst));
509 }
510};
511
512
513template<class T>
515 static inline T composeChannel(T src, T dst) {
516 using namespace Arithmetic;
517
518 qreal fsrc = scale<qreal>(src);
519 qreal fdst = scale<qreal>(dst);
520
521 if(fsrc > 0.5) {
522 // lerp(dst, sqrt(dst), (2.0 * fsrc - 1.0))
523 return scale<T>(fdst + (2.0 * fsrc - 1.0) * (sqrt(fdst) - fdst));
524 }
525
526 // lerp(dst, dst^2, (1.0 - 2.0 * fsrc))
527 return scale<T>(fdst - (1.0 - 2.0*fsrc) * fdst * (1.0 - fdst));
528 }
529};
530
531template<class T,
532 template <typename> typename ClampPolicy>
534 static inline T composeChannel(T src, T dst) {
535 using namespace Arithmetic;
536 using clamp_policy = ClampPolicy<T>;
537 using composite_type = typename KoColorSpaceMathsTraits<T>::compositetype;
538
539 if (src < halfValue<T>()) {
540 if (isZeroValueStrict(src)) {
541 return isUnitValueClampedStrict(dst) ? clamp_policy::clampResultAllowNegative(dst) : zeroValue<T>();
542 }
543
544 // min(1,max(0,1-(1-dst) / (2*src)))
545 composite_type src2 = composite_type(src) + src;
546 composite_type dsti = inv(dst);
547 return clamp_policy::clampResult(
548 unitValue<T>() -
549 clamp_policy::fixInfiniteAfterDivision(
550 divideInCompositeSpace<T>(dsti, src2)
551 )
552 );
553 }
554
555 if (isUnitValueStrict(src)) {
556 return isZeroValueClampedStrict(dst) ? zeroValue<T>() : unitValue<T>();
557 }
558
559 // min(1,max(0, dst / (2*(1-src)))
560 composite_type srci2 = inv(src);
561 srci2 += srci2;
562 return clamp_policy::clampResultAllowNegative(
563 clamp_policy::fixInfiniteAfterDivision(
564 divideInCompositeSpace<T>(composite_type(dst), srci2)
565 )
566 );
567 }
568};
569
570template<class T>
572 static inline T composeChannel(T src, T dst) {
573 using namespace Arithmetic;
574 using composite_type = typename KoColorSpaceMathsTraits<T>::compositetype;
575
576 // TODO: verify that the formula is correct (the first max would be useless here)
577 // max(0, max(2*src-1, min(dst, 2*src)))
578 composite_type src2 = composite_type(src) + src;
579 composite_type a = qMin<composite_type>(dst, src2);
580 composite_type b = qMax<composite_type>(src2-Arithmetic::unitValue<T>(), a);
581
586 return b;
587 }
588};
589
590template<class T>
591inline T cfArcTangent(T src, T dst) {
592 using namespace Arithmetic;
593 return scale<T>(2.0 * atan2(scale<qreal>(src), scale<qreal>(dst)) / Arithmetic::pi);
594}
595
596template<class T>
597inline T cfAllanon(T src, T dst) {
598 using namespace Arithmetic;
599 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
600 // (dst + src) / 2 [or (dst + src) * 0.5]
601 return T((composite_type(src) + dst) * halfValue<T>() / unitValue<T>());
602}
603
604template<class T>
605inline T cfLinearLight(T src, T dst) {
606 using namespace Arithmetic;
607 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
608
609 // min(1,max(0,(dst + 2*src)-1))
610 return clamp<T>((composite_type(src) + src + dst) - unitValue<T>());
611}
612
613template<class T>
615 static inline T composeChannel(T src, T dst) {
616 using namespace Arithmetic;
617 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
618
619 const bool srcIsSafe = !isUnsafeAsDivisor(src);
620 const bool dstIsSafe = !isUnsafeAsDivisor(dst);
621
622 if (!srcIsSafe || !dstIsSafe) {
623 return zeroValue<T>();
624 }
625
626 // min(max(2 / (1/dst + 1/src), 0), 1)
627 composite_type unit = unitValue<T>();
628 composite_type s = div<T>(unit, src);
629 composite_type d = div<T>(unit, dst);
630
631 return clamp<T>((unit+unit) * unit / (d+s));
632 }
633};
634
635template<class T>
637 static inline T composeChannel(T src, T dst) {
638 using namespace Arithmetic;
639 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
640 // TODO: is the formula correct?
641 // 1 - abs(dst - src)
642 composite_type x = qAbs(composite_type(dst) - src);
643 return clamp<T>(x);
644 }
645};
646
647template<class T>
649 static inline T composeChannel(T src, T dst) {
650 using namespace Arithmetic;
651 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
652 return clampToSDR<T>(composite_type(dst) + src - halfValue<T>());
653 }
654};
655
656template<class T>
658 static inline T composeChannel(T src, T dst) {
659 using namespace Arithmetic;
660 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
661 return clampToSDR<T>(composite_type(dst) - src + halfValue<T>());
662 }
663};
664
665template<class T,
666 template <typename> typename ClampPolicy>
668 static inline T composeChannel(T src, T dst) {
669 using namespace Arithmetic;
670
676 return dst > halfValue<T>() ?
679 }
680};
681
682template<class T>
684 static inline T composeChannel(T src, T dst) {
685 using namespace Arithmetic;
686 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
687
688 const composite_type sum = composite_type(src) + dst;
689
690 return sum > unitValue<T>() ? unitValue<T>() : zeroValue<T>();
691 }
692};
693
694// Approximation of the hard mix mode used by photoshop in the brush texturing
695// In contrast to the normal hard mix, this produces antialiased edges, better
696// for texturing the brush dab, at least visually
697
698template<class T>
700 static inline T composeChannel(T src, T dst) {
701 using namespace Arithmetic;
702 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
703
704 const composite_type srcScaleFactor = static_cast<composite_type>(2);
705 const composite_type dstScaleFactor = static_cast<composite_type>(3);
706
707 return clampToSDR<T>(dstScaleFactor * dst - srcScaleFactor * inv(src));
708 }
709};
710
711template<class T>
713 static inline T composeChannel(T src, T dst) {
714 using namespace Arithmetic;
715
716 // min(1,max(0,abs(sqr(CB)-sqr(CT))))
717 qreal x = sqrt(scale<qreal>(dst)) - sqrt(scale<qreal>(src));
718 return scale<T>(qAbs(x));
719 }
720};
721
722template<class T>
724 static inline T composeChannel(T src, T dst) {
725 using namespace Arithmetic;
726
727 const qreal dstReal = scale<qreal>(dst);
728
729 if (isZeroValueFuzzy(dstReal)) {
730 return zeroValue<T>();
731 }
732
733 if (isUnitValueFuzzy(dstReal)) {
734 return unitValue<T>();
735 }
736
737 // power(dst, 1/src)
738
739 const qreal srcReal = scale<qreal>(src);
740 const qreal exponent = 1.0 / srcReal;
741
742 if (std::isfinite(exponent)) {
743 return scale<T>(
744 std::min<qreal>(pow(dstReal, exponent),
746 } else {
747 return zeroValue<T>();
748 }
749 }
750};
751
752template<class T>
754 static inline T composeChannel(T src, T dst) {
755 using namespace Arithmetic;
756
757 const qreal dstReal = scale<qreal>(dst);
758
759 if (isZeroValueFuzzy(dstReal)) {
760 return zeroValue<T>();
761 }
762
763 if (isUnitValueFuzzy(dstReal)) {
764 return unitValue<T>();
765 }
766
767 const qreal srcReal = scale<qreal>(src);
768
769 return scale<T>(pow(dstReal, srcReal));
770 }
771};
772
773template<class T>
775 static inline T composeChannel(T src, T dst) {
776 using namespace Arithmetic;
777 return inv(CFGammaDark<T>::composeChannel(inv(src),inv(dst)));
778 }
779};
780
781template<class T>
783 static inline T composeChannel(T src, T dst) {
784 using namespace Arithmetic;
785
786 if (isUnitValueFuzzy(src) && isUnitValueFuzzy(dst)) {
787 return unitValue<T>();
788 }
789
790 return scale<T>(sqrt(scale<qreal>(dst) * scale<qreal>(src)));
791 }
792};
793
794template<class T>
795inline T cfOver(T src, T dst) { Q_UNUSED(dst); return src; }
796
797template<class T>
799 static inline T composeChannel(T src, T dst) {
800 return CFHardLight<T>::composeChannel(dst,src);
801 }
802};
803
804template<class T>
805inline T cfMultiply(T src, T dst) { return Arithmetic::mul(src, dst); }
806
807template<class T,
808 template <typename> typename ClampPolicy>
810 static inline T composeChannel(T src, T dst) {
811 using namespace Arithmetic;
812 using clamp_policy = ClampPolicy<T>;
813 using composite_type = typename KoColorSpaceMathsTraits<T>::compositetype;
814
815 if (isZeroValueStrict(dst)) {
816 return zeroValue<T>();
817 }
818
819 if (isUnitValueStrict(src)) {
820 return clamp_policy::clippedMaxValue();
821 }
822
823 if(src >= halfValue<T>()) {
824 return clamp_policy::clampResultAllowNegative(
825 clamp_policy::fixInfiniteAfterDivision(
826 divideInCompositeSpace<T>(composite_type(dst),
827 2 * composite_type(inv(src)))
828 )
829 );
830 }
831
832 return clamp_policy::clampResultAllowNegative(
833 multiplyInCompositeSpace<T>(composite_type(dst),
834 2 * composite_type(src))
835 );
836 }
837};
838
839template<class T>
840inline T cfDifference(T src, T dst) { return qMax(src,dst) - qMin(src,dst); }
841
842template<class T>
843inline T cfScreen(T src, T dst) { return Arithmetic::unionShapeOpacity(src, dst); }
844
845template<class T>
846inline T cfDarkenOnly(T src, T dst) { return qMin(src, dst); }
847
848template<class T>
849inline T cfLightenOnly(T src, T dst) { return qMax(src, dst); }
850
851template<class T>
852inline T cfGlow(T src, T dst) {
853 using namespace Arithmetic;
854 // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
855
856 if (dst == unitValue<T>()) {
857 return unitValue<T>();
858 }
859
860 return clamp<T>(div(mul(src, src), inv(dst)));
861}
862
863template<class T>
864inline T cfReflect(T src, T dst) {
865 using namespace Arithmetic;
866
867
868 return clamp<T>(cfGlow(dst,src));
869}
870
871template<class T>
872inline T cfHeat(T src, T dst) {
873 using namespace Arithmetic;
874
875 if(src == unitValue<T>()) {
876 return unitValue<T>();
877 }
878
879 if(dst == zeroValue<T>()) {
880 return zeroValue<T>();
881 }
882
883 return inv(clamp<T>(div(mul(inv(src), inv(src)),dst)));
884}
885
886template<class T>
887inline T cfFreeze(T src, T dst) {
888 using namespace Arithmetic;
889
890 return (cfHeat(dst,src));
891}
892
893template<typename T>
895 static inline T composeChannel(T src, T dst) {
896 using namespace Arithmetic;
897 // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
898
899 if (isUnitValueFuzzy<T>(CFHardMixPhotoshop<T>::composeChannel(src,dst))) {
900 return cfHeat(src,dst);
901 }
902
903 if (isZeroValueFuzzy<T>(src)) {
904 return zeroValue<T>();
905 }
906
907 return (cfGlow(src,dst));
908 }
909};
910
911template<typename T>
913 static inline T composeChannel(T src, T dst) {
914 using namespace Arithmetic;
915
916 if (isUnitValueFuzzy<T>(CFHardMixPhotoshop<T>::composeChannel(src,dst))) {
917 return cfFreeze(src,dst);
918 }
919
920 if (isZeroValueFuzzy<T>(dst)) {
921 return zeroValue<T>();
922 }
923
924 return (cfReflect(src,dst));
925 }
926};
927
928template<typename T>
930 static inline T composeChannel(T src, T dst) {
931 using namespace Arithmetic;
932 // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
933
934 if(isUnitValueFuzzy<T>(dst)) {
935 return unitValue<T>();
936 }
937
938 if(isUnitValueFuzzy<T>(CFHardMixPhotoshop<T>::composeChannel(src, dst))) {
939 return cfGlow(src,dst);
940 }
941
942 return (cfHeat(src,dst));
943 }
944};
945
946template<class T>
947struct CFReeze : CFGleat<T>
948{
949 static inline T composeChannel(T src, T dst) {
950 return CFGleat<T>::composeChannel(dst,src);
951 }
952};
953
954template<class T>
955inline T cfFhyrd(T src, T dst) {
956 using namespace Arithmetic;
957
958 return (cfAllanon(CFFrect<T>::composeChannel(src,dst),
960}
961
962template<class T>
963inline T cfInterpolation(T src, T dst) {
964 using namespace Arithmetic;
965
966 qreal fsrc = scale<qreal>(src);
967 qreal fdst = scale<qreal>(dst);
968
969 if(dst == zeroValue<T>() && src == zeroValue<T>()) {
970 return zeroValue<T>();
971 }
972
973 return scale<T>(.5f-.25f*cos(pi*(fsrc))-.25f*cos(pi*(fdst)));
974}
975
976template<class T>
977inline T cfInterpolationB(T src, T dst) {
978 using namespace Arithmetic;
979
980 return cfInterpolation(cfInterpolation(src,dst),cfInterpolation(src,dst));
981}
982
983
984template<typename T>
986 static inline T composeChannel(T src, T dst) {
987 using namespace Arithmetic;
988 using namespace KoCompositeOpClampPolicy;
989
990 if (dst == unitValue<T>()) {
991 return unitValue<T>();
992 }
993 if (dst + src < unitValue<T>()) {
995 }
996 if (src == zeroValue<T>()) {
997 return zeroValue<T>();
998 }
999
1000 return inv(clamp<T>(div(inv(dst),src)/2));
1001 }
1002};
1003
1004template<class T>
1005inline T cfPenumbraD(T src, T dst) {
1006 using namespace Arithmetic;
1007
1008 if (dst == unitValue<T>()) {
1009 return unitValue<T>();
1010 }
1011
1012 return cfArcTangent(src,inv(dst));
1013}
1014
1015template<class T>
1016inline T cfPenumbraC(T src, T dst) {
1017 using namespace Arithmetic;
1018
1019 return cfPenumbraD(dst,src);
1020}
1021
1022
1023template<typename T>
1025 static inline T composeChannel(T src, T dst) {
1026 return CFPenumbraB<T>::composeChannel(dst, src);
1027 }
1028};
1029
1030template<class T>
1031inline T cfSoftLightIFSIllusions(T src, T dst) {
1032 using namespace Arithmetic;
1033
1034 qreal fsrc = scale<qreal>(src);
1035 qreal fdst = scale<qreal>(dst);
1036
1037 return scale<T>(pow(fdst,pow(2.0,(mul(2.0,.5f-fsrc)))));
1038}
1039
1040template<class T>
1041inline T cfSoftLightPegtopDelphi(T src, T dst) {
1042 using namespace Arithmetic;
1043
1044 return clamp<T>(cfAddition(mul(dst,cfScreen(src,dst)),mul(mul(src,dst),inv(dst))));
1045}
1046
1047
1048template<class T>
1050 static inline T composeChannel(T src, T dst) {
1051 using namespace Arithmetic;
1052 typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
1053
1054 composite_type unit = unitValue<T>();
1055 composite_type a = unit - src - dst;
1056 composite_type s = std::abs(a);
1057 composite_type d = unit - s;
1058
1059 return T(d);
1060 }
1061};
1062
1063template<class T>
1064inline T cfNor(T src, T dst) {
1065 using namespace Arithmetic;
1066
1067 return and(src,dst);
1068}
1069
1070template<class T>
1071inline T cfNand(T src, T dst) {
1072 using namespace Arithmetic;
1073
1074 return or(src,dst);
1075}
1076
1077template<class T>
1078inline T cfXor(T src, T dst) {
1079 using namespace Arithmetic;
1080
1081 return xor(src,dst);
1082}
1083
1084template<class T>
1085inline T cfXnor(T src, T dst) {
1086 using namespace Arithmetic;
1087
1088 return cfXor(src,inv(dst));
1089}
1090
1091template<class T>
1092inline T cfAnd(T src, T dst) {
1093 using namespace Arithmetic;
1094
1095 return cfNor(inv(src),inv(dst));
1096}
1097
1098template<class T>
1099inline T cfOr(T src, T dst) {
1100 using namespace Arithmetic;
1101
1102 return cfNand(inv(src),inv(dst));
1103}
1104
1105template<class T>
1106inline T cfConverse(T src, T dst) {
1107 using namespace Arithmetic;
1108
1109 return cfOr(inv(src),dst);
1110}
1111
1112template<class T>
1113inline T cfNotConverse(T src, T dst) {
1114 using namespace Arithmetic;
1115
1116 return cfAnd(src,inv(dst));
1117}
1118
1119template<class T>
1120inline T cfImplies(T src, T dst) {
1121 using namespace Arithmetic;
1122
1123 return cfOr(src,inv(dst));
1124}
1125
1126template<class T>
1127inline T cfNotImplies(T src, T dst) {
1128 using namespace Arithmetic;
1129
1130 return cfAnd(inv(src),dst);
1131}
1132
1133template<class T>
1134inline T cfPNormA(T src, T dst) {
1135 using namespace Arithmetic;
1136 //This is also known as P-Norm mode with factor of 2.3333 See IMBLEND image blending mode samples, and please see imblend.m file found on Additional Blending Mode thread at Phabricator. 1/2.3333 is .42875...
1137
1138 return clamp<T>(pow(pow((float)dst, 2.3333333333333333) + pow((float)src, 2.3333333333333333), 0.428571428571434));
1139}
1140
1141template<class T>
1142inline T cfPNormB(T src, T dst) {
1143 using namespace Arithmetic;
1144 //This is also known as P-Norm mode with factor of 2.3333 See IMBLEND image blending mode samples, and please see imblend.m file found on Additional Blending Mode thread at Phabricator. 1/2.3333 is .42875...
1145
1146 return clamp<T>(pow(pow(dst,4)+pow(src,4),0.25));
1147}
1148
1149template<class T>
1150inline T cfSuperLight(T src, T dst) {
1151 using namespace Arithmetic;
1152 //4.0 can be adjusted to taste. 4.0 is picked for being the best in terms of contrast and details. See imblend.m file.
1153
1154 qreal fsrc = scale<qreal>(src);
1155 qreal fdst = scale<qreal>(dst);
1156
1157 if (fsrc < .5) {
1158 return scale<T>(inv(pow(pow(inv(fdst),2.875)+pow(inv(2.0*fsrc),2.875),1.0/2.875)));
1159 }
1160
1161 return scale<T>(pow(pow(fdst,2.875)+pow(2.0*fsrc-1.0,2.875),1.0/2.875));
1162}
1163
1164template<class T>
1165inline T cfTintIFSIllusions(T src, T dst) {
1166 using namespace Arithmetic;
1167 //Known as Light Blending mode found in IFS Illusions. Picked this name because it results into a very strong tint, and has better naming convention.
1168
1169 qreal fsrc = scale<qreal>(src);
1170 qreal fdst = scale<qreal>(dst);
1171
1172 return scale<T>(fsrc*inv(fdst)+sqrt(fdst));
1173}
1174
1175template<class T>
1176inline T cfShadeIFSIllusions(T src, T dst) {
1177 using namespace Arithmetic;
1178 //Known as Shadow Blending mode found in IFS Illusions. Picked this name because it is the opposite of Tint (IFS Illusion Blending mode).
1179
1180 qreal fsrc = scale<qreal>(src);
1181 qreal fdst = scale<qreal>(dst);
1182
1183 return scale<T>(inv((inv(fdst)*fsrc)+sqrt(inv(fsrc))));
1184}
1185
1186template<class T>
1187inline T cfFogLightenIFSIllusions(T src, T dst) {
1188 using namespace Arithmetic;
1189 //Known as Bright Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradient.
1190
1191 qreal fsrc = scale<qreal>(src);
1192 qreal fdst = scale<qreal>(dst);
1193
1194 if (fsrc < .5) {
1195 return scale<T>(inv(inv(fsrc)*fsrc)-inv(fdst)*inv(fsrc));
1196 }
1197
1198 return scale<T>(fsrc-inv(fdst)*inv(fsrc)+pow(inv(fsrc),2));
1199}
1200
1201template<class T>
1202inline T cfFogDarkenIFSIllusions(T src, T dst) {
1203 using namespace Arithmetic;
1204 //Known as Dark Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradient.
1205
1206 qreal fsrc = scale<qreal>(src);
1207 qreal fdst = scale<qreal>(dst);
1208
1209 if (fsrc < .5) {
1210 return scale<T>(inv(fsrc)*fsrc+fsrc*fdst);
1211 }
1212
1213 return scale<T>(fsrc*fdst+fsrc-pow(fsrc,2));
1214}
1215
1216template<class T>
1217inline T cfModulo(T src, T dst) {
1218 using namespace Arithmetic;
1219
1220 return mod(dst,src);
1221}
1222
1223template<class T>
1224inline T cfModuloShift(T src, T dst) {
1225 using namespace Arithmetic;
1226 qreal fsrc = scale<qreal>(src);
1227 qreal fdst = scale<qreal>(dst);
1228
1229 if (fsrc == 1.0 && fdst == 0.0) {
1230 return scale<T>(0.0);
1231 }
1232
1233
1234 return scale<T>(mod((fdst+fsrc),1.0000000000));
1235}
1236
1237template<class T>
1238inline T cfModuloShiftContinuous(T src, T dst) {
1239 using namespace Arithmetic;
1240 //This blending mode do not behave like difference/equivalent with destination layer inverted if you use group layer on addition while the content of group layer contains several addition-mode layers, it works as expected on float images. So, no need to change this.
1241 qreal fsrc = scale<qreal>(src);
1242 qreal fdst = scale<qreal>(dst);
1243
1244 if (fsrc == 1.0 && fdst == 0.0) {
1245 return scale<T>(1.0);
1246 }
1247
1248 return scale<T>((int(ceil(fdst+fsrc)) % 2 != 0) || (fdst == zeroValue<T>()) ? cfModuloShift(fsrc,fdst) : inv(cfModuloShift(fsrc,fdst)));
1249}
1250
1251template<class T>
1252inline T cfDivisiveModulo(T src, T dst) {
1253 using namespace Arithmetic;
1254 //I have to use 1.00000 as unitValue failed to work for those area.
1255
1256 qreal fsrc = scale<qreal>(src);
1257 qreal fdst = scale<qreal>(dst);
1258
1259 if (fsrc == zeroValue<T>()) {
1260 return scale<T>(mod(((1.0000000000/epsilon<T>()) * fdst),1.0000000000));
1261 }
1262
1263 return scale<T>(mod(((1.0000000000/fsrc) * fdst),1.0000000000));
1264}
1265
1266template<class T>
1267inline T cfDivisiveModuloContinuous(T src, T dst) {
1268 using namespace Arithmetic;
1269
1270 qreal fsrc = scale<qreal>(src);
1271 qreal fdst = scale<qreal>(dst);
1272
1273 if (fdst == zeroValue<T>()) {
1274 return zeroValue<T>();
1275 }
1276
1277 if (fsrc == zeroValue<T>()) {
1278 return cfDivisiveModulo(fsrc,fdst);
1279 }
1280
1281
1282 return scale<T>( int(ceil(fdst/fsrc)) % 2 != 0 ? cfDivisiveModulo(fsrc,fdst) : inv(cfDivisiveModulo(fsrc,fdst)));
1283}
1284
1285template<class T>
1286inline T cfModuloContinuous(T src, T dst) {
1287 using namespace Arithmetic;
1288
1289 return cfMultiply(cfDivisiveModuloContinuous(src,dst),src);
1290}
1291
1292template<class T>
1293inline T cfEasyDodge(T src, T dst) {
1294 using namespace Arithmetic;
1295 // The 13 divided by 15 can be adjusted to taste. See imgblend.m
1296
1297 qreal fsrc = scale<qreal>(src);
1298 qreal fdst = scale<qreal>(dst);
1299
1300 if (fsrc == 1.0) {
1301 return scale<T>(1.0);}
1302
1303
1304 return scale<T>(pow(fdst,mul(inv(fsrc != 1.0 ? fsrc : .999999999999),1.039999999)));
1305}
1306
1307template<class T>
1308inline T cfEasyBurn(T src, T dst) {
1309 using namespace Arithmetic;
1310 // The 13 divided by 15 can be adjusted to taste. See imgblend.m
1311
1312 qreal fsrc = scale<qreal>(src);
1313 qreal fdst = scale<qreal>(dst);
1314
1315
1316 return scale<T>(inv(pow(inv(fsrc != 1.0 ? fsrc : .999999999999),mul(fdst,1.039999999))));
1317}
1318
1319template<typename T>
1321 static inline T composeChannel(T src, T dst) {
1322 using namespace Arithmetic;
1323
1324 if (isZeroValueFuzzy<T>(src)) {
1325 return zeroValue<T>();
1326 }
1327
1328 return clamp<T>(isUnitValueFuzzy<T>(CFHardMixPhotoshop<T>::composeChannel(inv(src),dst)) ?
1331 }
1332};
1333
1334
1335template<class HSXType, class TReal>
1336inline void cfAdditionSAI(TReal src, TReal sa, TReal& dst, TReal& da)
1337{
1338 using namespace Arithmetic;
1339 typedef typename KoColorSpaceMathsTraits<TReal>::compositetype composite_type;
1340
1341 Q_UNUSED(da);
1342 composite_type newsrc;
1343 newsrc = mul(src, sa);
1344 dst = clamp<TReal>(newsrc + dst);
1345}
1346
1347
1348
1349#endif // KOCOMPOSITEOP_FUNCTIONS_H_
T cfNotImplies(T src, T dst)
T cfMultiply(T src, T dst)
T cfDarkenOnly(T src, T dst)
T cfPNormA(T src, T dst)
T cfModuloShiftContinuous(T src, T dst)
T cfModuloContinuous(T src, T dst)
T cfInterpolation(T src, T dst)
T cfLinearLight(T src, T dst)
T cfAnd(T src, T dst)
T cfOr(T src, T dst)
T cfSoftLightPegtopDelphi(T src, T dst)
T cfPenumbraC(T src, T dst)
T cfOver(T src, T dst)
T cfSoftLightIFSIllusions(T src, T dst)
T cfScreen(T src, T dst)
T cfXnor(T src, T dst)
T cfAddition(T src, T dst)
T cfDivisiveModuloContinuous(T src, T dst)
T cfNand(T src, T dst)
T cfPenumbraD(T src, T dst)
T cfPNormB(T src, T dst)
T cfFogLightenIFSIllusions(T src, T dst)
T cfHeat(T src, T dst)
T cfDifference(T src, T dst)
T cfEasyBurn(T src, T dst)
T cfFhyrd(T src, T dst)
T cfConverse(T src, T dst)
T cfSubtract(T src, T dst)
T cfFogDarkenIFSIllusions(T src, T dst)
T cfLightenOnly(T src, T dst)
void cfAdditionSAI(TReal src, TReal sa, TReal &dst, TReal &da)
T cfNotConverse(T src, T dst)
T cfXor(T src, T dst)
T cfModulo(T src, T dst)
T cfImplies(T src, T dst)
T cfModuloShift(T src, T dst)
T cfInterpolationB(T src, T dst)
T cfArcTangent(T src, T dst)
T cfSuperLight(T src, T dst)
T cfGlow(T src, T dst)
T cfFreeze(T src, T dst)
T cfShadeIFSIllusions(T src, T dst)
T cfNor(T src, T dst)
T cfDivisiveModulo(T src, T dst)
T cfTintIFSIllusions(T src, T dst)
T cfAllanon(T src, T dst)
T cfEasyDodge(T src, T dst)
T cfReflect(T src, T dst)
qreal D(qreal t, const QPointF &P0, const QPointF &P1, const QPointF &P2, const QPointF &P3, const QPointF &p)
QPointF lerp(const QPointF &p1, const QPointF &p2, qreal t)
T mul(T a, T b)
T unionShapeOpacity(T a, T b)
static const qreal pi
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static T composeChannel(T src, T dst)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static T composeChannel(T src, T dst)
static T composeChannel(T src, T dst)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static void composeChannels(float srcR, float srcG, float srcB, float &dstR, float &dstG, float &dstB)
static T composeChannel(T src, T dst)