File indexing completed on 2024-12-22 04:11:39

0001 /*
0002  *  SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #ifndef KOCOMPOSITEOP_FUNCTIONS_H_
0008 #define KOCOMPOSITEOP_FUNCTIONS_H_
0009 
0010 #include <KoColorSpaceMaths.h>
0011 
0012 #include <type_traits>
0013 #include <cmath>
0014 
0015 #ifdef HAVE_OPENEXR
0016 #include "half.h"
0017 #endif
0018 
0019 template<class HSXType, class TReal>
0020 inline void cfReorientedNormalMapCombine(TReal srcR, TReal srcG, TReal srcB, TReal& dstR, TReal& dstG, TReal& dstB)
0021 {
0022     // see https://blog.selfshadow.com/publications/blending-in-detail/ by Barre-Brisebois and Hill
0023     TReal tx = 2*srcR-1;
0024     TReal ty = 2*srcG-1;
0025     TReal tz = 2*srcB;
0026     TReal ux = -2*dstR+1;
0027     TReal uy = -2*dstG+1;
0028     TReal uz = 2*dstB-1;
0029     TReal k = (tx*ux+ty*uy+tz*uz)/tz; // dot(t,u)/t.z
0030     TReal rx = tx*k-ux;
0031     TReal ry = ty*k-uy;
0032     TReal rz = tz*k-uz;
0033     k = 1/sqrt(rx*rx+ry*ry+rz*rz); // normalize result
0034     rx *= k;
0035     ry *= k;
0036     rz *= k;
0037     dstR = rx*0.5+0.5;
0038     dstG = ry*0.5+0.5;
0039     dstB = rz*0.5+0.5;
0040 }
0041 
0042 template<class HSXType, class TReal>
0043 inline void cfColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0044     TReal lum = getLightness<HSXType>(dr, dg, db);
0045     dr = sr;
0046     dg = sg;
0047     db = sb;
0048     setLightness<HSXType>(dr, dg, db, lum);
0049 }
0050 
0051 template<class HSXType, class TReal>
0052 inline void cfLambertLighting(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db)
0053 {
0054     TReal tr = sr * dr * (1.0 / 0.215686);
0055     TReal tg = sg * dg * (1.0 / 0.215686);
0056     TReal tb = sb * db * (1.0 / 0.215686);
0057 
0058     if (tr > 1.0) {
0059         dr = 1.0 + (tr - 1.0) * (tr - 1.0) * 0.01925;
0060     }
0061     else {
0062         dr = tr;
0063     }
0064 
0065     if (tg > 1.0) {
0066         dg = 1.0 + (tg - 1.0) * (tg - 1.0) * 0.01925;
0067     }
0068     else {
0069         dg = tg;
0070     }
0071 
0072     if (tb > 1.0) {
0073         db = 1.0 + (tb - 1.0) * (tb - 1.0) * 0.01925;
0074     }
0075     else {
0076         db = tb;
0077     }
0078 
0079 
0080     ToneMapping<HSXType, TReal>(dr, dg, db);
0081 }
0082 
0083 template<class HSXType, class TReal>
0084 inline void cfLambertLightingGamma2_2(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0085     TReal tr = sr * dr * 2.0;
0086     TReal tg = sg * dg * 2.0;
0087     TReal tb = sb * db * 2.0;
0088 
0089     if (tr > 1.0) {
0090         dr = 1.0 + (tr - 1.0) * (tr - 1.0) * 0.4;
0091     }
0092     else {
0093         dr = tr;
0094     }
0095 
0096     if (tg > 1.0) {
0097         dg = 1.0 + (tg - 1.0) * (tg - 1.0) * 0.4;
0098     }
0099     else {
0100         dg = tg;
0101     }
0102 
0103     if (tb > 1.0) {
0104         db = 1.0 + (tb - 1.0) * (tb - 1.0) * 0.4;
0105     }
0106     else {
0107         db = tb;
0108     }
0109 
0110     ToneMapping<HSXType, TReal>(dr, dg, db);
0111 }
0112 
0113 template<class HSXType, class TReal>
0114 inline void cfLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0115     setLightness<HSXType>(dr, dg, db, getLightness<HSXType>(sr, sg, sb));
0116 }
0117 
0118 template<class HSXType, class TReal>
0119 inline void cfIncreaseLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0120     addLightness<HSXType>(dr, dg, db, getLightness<HSXType>(sr, sg, sb));
0121 }
0122 
0123 template<class HSXType, class TReal>
0124 inline void cfDecreaseLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0125     addLightness<HSXType>(dr, dg, db, getLightness<HSXType>(sr, sg, sb) - TReal(1.0));
0126 }
0127 
0128 template<class HSXType, class TReal>
0129 inline void cfSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0130     TReal sat   = getSaturation<HSXType>(sr, sg, sb);
0131     TReal light = getLightness<HSXType>(dr, dg, db);
0132     setSaturation<HSXType>(dr, dg, db, sat);
0133     setLightness<HSXType>(dr, dg, db, light);
0134 }
0135 
0136 template<class HSXType, class TReal>
0137 inline void cfIncreaseSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0138     using namespace Arithmetic;
0139     TReal sat   = lerp(getSaturation<HSXType>(dr,dg,db), unitValue<TReal>(), getSaturation<HSXType>(sr,sg,sb));
0140     TReal light = getLightness<HSXType>(dr, dg, db);
0141     setSaturation<HSXType>(dr, dg, db, sat);
0142     setLightness<HSXType>(dr, dg, db, light);
0143 }
0144 
0145 template<class HSXType, class TReal>
0146 inline void cfDecreaseSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0147     using namespace Arithmetic;
0148     TReal sat   = lerp(zeroValue<TReal>(), getSaturation<HSXType>(dr,dg,db), getSaturation<HSXType>(sr,sg,sb));
0149     TReal light = getLightness<HSXType>(dr, dg, db);
0150     setSaturation<HSXType>(dr, dg, db, sat);
0151     setLightness<HSXType>(dr, dg, db, light);
0152 }
0153 
0154 template<class HSXType, class TReal>
0155 inline void cfHue(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0156     TReal sat = getSaturation<HSXType>(dr, dg, db);
0157     TReal lum = getLightness<HSXType>(dr, dg, db);
0158     dr = sr;
0159     dg = sg;
0160     db = sb;
0161     setSaturation<HSXType>(dr, dg, db, sat);
0162     setLightness<HSXType>(dr, dg, db, lum);
0163 }
0164 
0165 template<class HSXType, class TReal>
0166 inline void cfTangentNormalmap(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0167     using namespace Arithmetic;
0168     TReal half=halfValue<TReal>();
0169     
0170     dr = sr+(dr-half);
0171     dg = sg+(dg-half);
0172     db = sb+(db-unitValue<TReal>());
0173 } 
0174     
0175 template<class HSXType, class TReal>
0176 inline void cfDarkerColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0177     
0178     TReal lum = getLightness<HSXType>(dr, dg, db);
0179     TReal lum2 = getLightness<HSXType>(sr, sg, sb);
0180     if (lum<lum2) {
0181         sr = dr;
0182         sg = dg;
0183         sb = db;
0184     }
0185     else {
0186         dr = sr;
0187         dg = sg;
0188         db = sb;
0189     }
0190 
0191 }
0192 
0193 template<class HSXType, class TReal>
0194 inline void cfLighterColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
0195     
0196     TReal lum = getLightness<HSXType>(dr, dg, db);
0197     TReal lum2 = getLightness<HSXType>(sr, sg, sb);
0198     if (lum>lum2) {
0199         sr = dr;
0200         sg = dg;
0201         sb = db;
0202     }
0203     else {
0204         dr = sr;
0205         dg = sg;
0206         db = sb;
0207     }
0208 }
0209 
0210 template<class T>
0211 inline T colorBurnHelper(T src, T dst) {
0212     using namespace Arithmetic;
0213     // Handle the case where the denominator is 0. See color dodge for a
0214     // detailed explanation
0215     if(src == zeroValue<T>()) {
0216         return dst == unitValue<T>() ? zeroValue<T>() : KoColorSpaceMathsTraits<T>::max;
0217     }
0218     return clamp<T>(div(inv(dst), src));
0219 }
0220 
0221 // Integer version of color burn
0222 template<class T>
0223 inline typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
0224 cfColorBurn(T src, T dst) {
0225     using namespace Arithmetic;
0226     return inv(colorBurnHelper(src, dst));
0227 }
0228 
0229 // Floating point version of color burn
0230 template<class T>
0231 inline typename std::enable_if<!std::numeric_limits<T>::is_integer, T>::type
0232 cfColorBurn(T src, T dst) {
0233     using namespace Arithmetic;
0234     const T result = colorBurnHelper(src, dst);
0235     // Constantly dividing by small numbers can quickly make the result
0236     // become infinity or NaN, so we check that and correct (kind of clamping)
0237     return inv(std::isfinite(result) ? result : KoColorSpaceMathsTraits<T>::max);
0238 }
0239 
0240 template<class T>
0241 inline T cfLinearBurn(T src, T dst) {
0242     using namespace Arithmetic;
0243     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0244     return clamp<T>(composite_type(src) + dst - unitValue<T>());
0245 }
0246 
0247 template<class T>
0248 inline T colorDodgeHelper(T src, T dst) {
0249     using namespace Arithmetic;
0250     // Handle the case where the denominator is 0.
0251     // When src is 1 then the denominator (1 - src) becomes 0, and to avoid
0252     // dividing by 0 we treat the denominator as an infinitely small number,
0253     // so the result of the formula would approach infinity. As in the generic
0254     // case, that result is clamped to the maximum value (which for integer
0255     // types is the same as the unit value).
0256     // Another special case is when both numerator and denominator are 0. In
0257     // this case we also treat the denominator as an infinitely small number,
0258     // and the numerator can remain as 0, so dividing 0 over a number (no matter
0259     // how small it is) gives 0.
0260     if (src == unitValue<T>()) {
0261         return dst == zeroValue<T>() ? zeroValue<T>() : KoColorSpaceMathsTraits<T>::max;
0262     }
0263     return Arithmetic::clamp<T>(div(dst, inv(src)));
0264 }
0265 
0266 // Integer version of color dodge
0267 template<class T>
0268 inline typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
0269 cfColorDodge(T src, T dst) {
0270     return colorDodgeHelper(src, dst);
0271 }
0272 
0273 // Floating point version of color dodge
0274 template<class T>
0275 inline typename std::enable_if<!std::numeric_limits<T>::is_integer, T>::type
0276 cfColorDodge(T src, T dst) {
0277     const T result = colorDodgeHelper(src, dst);
0278     // Constantly dividing by small numbers can quickly make the result
0279     // become infinity or NaN, so we check that and correct (kind of clamping)
0280     return std::isfinite(result) ? result : KoColorSpaceMathsTraits<T>::max;
0281 }
0282 
0283 template<class T>
0284 inline T cfAddition(T src, T dst) {
0285     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0286     return Arithmetic::clamp<T>(composite_type(src) + dst);
0287 }
0288 
0289 template<class T>
0290 inline T cfSubtract(T src, T dst) {
0291     using namespace Arithmetic;
0292     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0293     return clamp<T>(composite_type(dst) - src);
0294 }
0295 
0296 template<class T>
0297 inline T cfInverseSubtract(T src, T dst) {
0298     using namespace Arithmetic;
0299     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0300     return clamp<T>(composite_type(dst) - inv(src));
0301 }
0302 
0303 template<class T>
0304 inline T cfExclusion(T src, T dst) {
0305     using namespace Arithmetic;
0306     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0307     
0308     composite_type x = mul(src, dst);
0309     return clamp<T>(composite_type(dst) + src - (x + x));
0310 }
0311 
0312 template<class T>
0313 inline T cfDivide(T src, T dst) {
0314     using namespace Arithmetic;
0315     //typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0316     
0317     if(isUnsafeAsDivisor(src))
0318         return (dst == zeroValue<T>()) ? zeroValue<T>() : unitValue<T>();
0319     
0320     return clamp<T>(div(dst, src));
0321 }
0322 
0323 template<class T>
0324 inline T cfHardLight(T src, T dst) {
0325     using namespace Arithmetic;
0326     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0327     
0328     composite_type src2 = composite_type(src) + src;
0329     
0330     if(src > halfValue<T>()) {
0331         // screen(src*2.0 - 1.0, dst)
0332         src2 -= unitValue<T>();
0333 
0334         // src2 is guaranteed to be smaller than unitValue<T>() now
0335         return Arithmetic::unionShapeOpacity(T(src2), dst);
0336     }
0337     
0338     // src2 is guaranteed to be smaller than unitValue<T>() due to 'if'
0339     return Arithmetic::mul(T(src2), dst);
0340 }
0341 
0342 template<class T>
0343 inline T cfSoftLightSvg(T src, T dst) {
0344     using namespace Arithmetic;
0345 
0346     qreal fsrc = scale<qreal>(src);
0347     qreal fdst = scale<qreal>(dst);
0348 
0349     if(fsrc > 0.5) {
0350         qreal D = (fdst > 0.25) ? sqrt(fdst) : ((16.0*fdst - 12.0)*fdst + 4.0)*fdst;
0351         return scale<T>(fdst + (2.0*fsrc - 1.0) * (D - fdst));
0352     }
0353 
0354     return scale<T>(fdst - (1.0 - 2.0 * fsrc) * fdst * (1.0 - fdst));
0355 }
0356 
0357 
0358 template<class T>
0359 inline T cfSoftLight(T src, T dst) {
0360     using namespace Arithmetic;
0361     
0362     qreal fsrc = scale<qreal>(src);
0363     qreal fdst = scale<qreal>(dst);
0364     
0365     if(fsrc > 0.5) {
0366         return scale<T>(fdst + (2.0 * fsrc - 1.0) * (sqrt(fdst) - fdst));
0367     }
0368     
0369     return scale<T>(fdst - (1.0 - 2.0*fsrc) * fdst * (1.0 - fdst));
0370 }
0371 
0372 template<class T>
0373 inline T cfVividLight(T src, T dst) {
0374     using namespace Arithmetic;
0375     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0376     
0377     if(src < halfValue<T>()) {
0378         if(isUnsafeAsDivisor(src))
0379             return (dst == unitValue<T>()) ? unitValue<T>() : zeroValue<T>();
0380 
0381         // min(1,max(0,1-(1-dst) / (2*src)))
0382         composite_type src2 = composite_type(src) + src;
0383         composite_type dsti = inv(dst);
0384         return clamp<T>(unitValue<T>() - (dsti * unitValue<T>() / src2));
0385     }
0386     
0387     if(src == unitValue<T>())
0388         return (dst == zeroValue<T>()) ? zeroValue<T>() : unitValue<T>();
0389     
0390     // min(1,max(0, dst / (2*(1-src)))
0391     composite_type srci2 = inv(src);
0392     srci2 += srci2;
0393     return clamp<T>(composite_type(dst) * unitValue<T>() / srci2);
0394 }
0395 
0396 template<class T>
0397 inline T cfPinLight(T src, T dst) {
0398     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0399     // TODO: verify that the formula is correct (the first max would be useless here)
0400     // max(0, max(2*src-1, min(dst, 2*src)))
0401     composite_type src2 = composite_type(src) + src;
0402     composite_type a    = qMin<composite_type>(dst, src2);
0403     composite_type b    = qMax<composite_type>(src2-Arithmetic::unitValue<T>(), a);
0404     return T(b);
0405 }
0406 
0407 template<class T>
0408 inline T cfArcTangent(T src, T dst) {
0409     using namespace Arithmetic;
0410     
0411     if(dst == zeroValue<T>())
0412         return (src == zeroValue<T>()) ? zeroValue<T>() : unitValue<T>();
0413     
0414     return scale<T>(2.0 * atan(scale<qreal>(src) / scale<qreal>(dst)) / Arithmetic::pi);
0415 }
0416 
0417 template<class T>
0418 inline T cfAllanon(T src, T dst) {
0419     using namespace Arithmetic;
0420     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0421     // (dst + src) / 2   [or (dst + src) * 0.5]
0422     return T((composite_type(src) + dst) * halfValue<T>() / unitValue<T>());
0423 }
0424 
0425 template<class T>
0426 inline T cfLinearLight(T src, T dst) {
0427     using namespace Arithmetic;
0428     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0429     
0430     // min(1,max(0,(dst + 2*src)-1))
0431     return clamp<T>((composite_type(src) + src + dst) - unitValue<T>());
0432 }
0433 
0434 template<class T>
0435 inline T cfParallel(T src, T dst) {
0436     using namespace Arithmetic;
0437     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0438     
0439     const bool srcIsSafe = !isUnsafeAsDivisor(src);
0440     const bool dstIsSafe = !isUnsafeAsDivisor(dst);
0441 
0442     if (!srcIsSafe || !dstIsSafe) {
0443         return zeroValue<T>();
0444     }
0445 
0446     // min(max(2 / (1/dst + 1/src), 0), 1)
0447     composite_type unit = unitValue<T>();
0448     composite_type s    = div<T>(unit, src);
0449     composite_type d    = div<T>(unit, dst);
0450 
0451     return clamp<T>((unit+unit) * unit / (d+s));
0452 }
0453 
0454 template<class T>
0455 inline T cfEquivalence(T src, T dst) {
0456     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0457     // 1 - abs(dst - src)
0458     composite_type x = composite_type(dst) - src;
0459     return (x < Arithmetic::zeroValue<T>()) ? T(-x) : T(x);
0460 }
0461 
0462 template<class T>
0463 inline T cfGrainMerge(T src, T dst) {
0464     using namespace Arithmetic;
0465     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0466     return clamp<T>(composite_type(dst) + src - halfValue<T>());
0467 }
0468 
0469 template<class T>
0470 inline T cfGrainExtract(T src, T dst) {
0471     using namespace Arithmetic;
0472     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0473     return clamp<T>(composite_type(dst) - src + halfValue<T>());
0474 }
0475 
0476 template<class T>
0477 inline T cfHardMix(T src, T dst) {
0478     return (dst > Arithmetic::halfValue<T>()) ? cfColorDodge(src,dst) : cfColorBurn(src,dst);
0479 }
0480 
0481 template<class T>
0482 inline T cfHardMixPhotoshop(T src, T dst) {
0483     using namespace Arithmetic;
0484     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0485 
0486     const composite_type sum = composite_type(src) + dst;
0487 
0488     return sum > unitValue<T>() ? unitValue<T>() : zeroValue<T>();
0489 }
0490 
0491 // Approximation of the hard mix mode used by photoshop in the brush texturing
0492 // In contrast to the normal hard mix, this produces antialiased edges, better
0493 // for texturing the brush dab, at least visually
0494 template<class T>
0495 inline T cfHardMixSofterPhotoshop(T src, T dst) {
0496     using namespace Arithmetic;
0497     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0498 
0499     const composite_type srcScaleFactor = static_cast<composite_type>(2);
0500     const composite_type dstScaleFactor = static_cast<composite_type>(3);
0501 
0502     return clamp<T>(dstScaleFactor * dst - srcScaleFactor * inv(src));
0503 }
0504 
0505 template<class T>
0506 inline T cfAdditiveSubtractive(T src, T dst) {
0507     using namespace Arithmetic;
0508     // min(1,max(0,abs(sqr(CB)-sqr(CT))))
0509     qreal x = sqrt(scale<qreal>(dst)) - sqrt(scale<qreal>(src));
0510     return scale<T>((x < 0.0) ? -x : x);
0511 }
0512 
0513 template<class T>
0514 inline T cfGammaDark(T src, T dst) {
0515     using namespace Arithmetic;
0516     
0517     if(src == zeroValue<T>())
0518         return zeroValue<T>();
0519     
0520     // power(dst, 1/src)
0521     return scale<T>(pow(scale<qreal>(dst), 1.0/scale<qreal>(src)));
0522 }
0523 
0524 template<class T>
0525 inline T cfGammaLight(T src, T dst) {
0526     using namespace Arithmetic;
0527     return scale<T>(pow(scale<qreal>(dst), scale<qreal>(src)));
0528 }
0529 
0530 template<class T>
0531 inline T cfGammaIllumination(T src, T dst) {
0532     using namespace Arithmetic;
0533     return inv(cfGammaDark(inv(src),inv(dst)));
0534 }
0535 
0536 template<class T>
0537 inline T cfGeometricMean(T src, T dst) {
0538     using namespace Arithmetic;
0539     return scale<T>(sqrt(scale<qreal>(dst) * scale<qreal>(src)));
0540 }
0541 
0542 template<class T>
0543 inline T cfOver(T src, T dst) { Q_UNUSED(dst); return src; }
0544 
0545 template<class T>
0546 inline T cfOverlay(T src, T dst) { return cfHardLight(dst, src); }
0547 
0548 template<class T>
0549 inline T cfMultiply(T src, T dst) { return Arithmetic::mul(src, dst); }
0550 
0551 template<class T>
0552 inline T cfHardOverlay(T src, T dst) {
0553     using namespace Arithmetic;
0554 
0555     qreal fsrc = scale<qreal>(src);
0556     qreal fdst = scale<qreal>(dst);
0557     
0558     if (fsrc == 1.0) {
0559         return scale<T>(1.0);}
0560 
0561     if(fsrc > 0.5) {
0562         return scale<T>(cfDivide(inv(2.0 * fsrc - 1.0), fdst));
0563     }
0564     return scale<T>(mul(2.0 * fsrc, fdst));
0565 }
0566 
0567 template<class T>
0568 inline T cfDifference(T src, T dst) { return qMax(src,dst) - qMin(src,dst); }
0569 
0570 template<class T>
0571 inline T cfScreen(T src, T dst) { return Arithmetic::unionShapeOpacity(src, dst); }
0572 
0573 template<class T>
0574 inline T cfDarkenOnly(T src, T dst) { return qMin(src, dst); }
0575 
0576 template<class T>
0577 inline T cfLightenOnly(T src, T dst) { return qMax(src, dst); }
0578 
0579 template<class T>
0580 inline T cfGlow(T src, T dst) {
0581     using namespace Arithmetic;
0582         // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
0583 
0584     if (dst == unitValue<T>()) {
0585         return unitValue<T>();
0586     }
0587 
0588     return clamp<T>(div(mul(src, src), inv(dst)));
0589 }
0590 
0591 template<class T>
0592 inline T cfReflect(T src, T dst) {
0593     using namespace Arithmetic;
0594     
0595     
0596     return clamp<T>(cfGlow(dst,src));
0597 }
0598 
0599 template<class T>
0600 inline T cfHeat(T src, T dst) {
0601     using namespace Arithmetic;
0602     
0603     if(src == unitValue<T>()) {
0604         return unitValue<T>();
0605     }
0606 
0607     if(dst == zeroValue<T>()) {
0608         return zeroValue<T>();
0609     }
0610 
0611     return inv(clamp<T>(div(mul(inv(src), inv(src)),dst)));
0612 }
0613 
0614 template<class T>
0615 inline T cfFreeze(T src, T dst) {
0616     using namespace Arithmetic;
0617     
0618     return (cfHeat(dst,src)); 
0619 }
0620 
0621 template<class T>
0622 inline T cfHelow(T src, T dst) {
0623     using namespace Arithmetic;
0624     // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
0625     
0626     if (cfHardMixPhotoshop(src,dst) == unitValue<T>()) {
0627         return cfHeat(src,dst);
0628     }
0629     
0630     if (src == zeroValue<T>()) {
0631         return zeroValue<T>();
0632     }
0633 
0634     return (cfGlow(src,dst));
0635 }
0636 
0637 template<class T>
0638 inline T cfFrect(T src, T dst) {
0639     using namespace Arithmetic;
0640 
0641     if (cfHardMixPhotoshop(src,dst) == unitValue<T>()) {
0642         return cfFreeze(src,dst);
0643     }
0644 
0645     if (dst == zeroValue<T>()) {
0646         return zeroValue<T>();
0647     }
0648 
0649     return (cfReflect(src,dst));
0650 }
0651 
0652 template<class T>
0653 inline T cfGleat(T src, T dst) {
0654     using namespace Arithmetic;
0655         // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
0656     
0657     if(dst == unitValue<T>()) {
0658         return unitValue<T>();
0659     }    
0660     
0661     if(cfHardMixPhotoshop(src,dst) == unitValue<T>()) {
0662         return cfGlow(src,dst);
0663     }
0664     
0665     return (cfHeat(src,dst));
0666 }
0667 
0668 template<class T>
0669 inline T cfReeze(T src, T dst) {
0670     using namespace Arithmetic;
0671     
0672     return (cfGleat(dst,src)); 
0673 }
0674 template<class T>
0675 inline T cfFhyrd(T src, T dst) {
0676     using namespace Arithmetic;
0677     
0678     return (cfAllanon(cfFrect(src,dst),cfHelow(src,dst))); 
0679 }
0680 
0681 template<class T>
0682 inline T cfInterpolation(T src, T dst) {
0683     using namespace Arithmetic;
0684 
0685     qreal fsrc = scale<qreal>(src);
0686     qreal fdst = scale<qreal>(dst);
0687     
0688     if(dst == zeroValue<T>() && src == zeroValue<T>()) {
0689         return zeroValue<T>();
0690     } 
0691 
0692     return scale<T>(.5f-.25f*cos(pi*(fsrc))-.25f*cos(pi*(fdst)));
0693 }
0694 
0695 template<class T>
0696 inline T cfInterpolationB(T src, T dst) {
0697     using namespace Arithmetic;
0698 
0699     return cfInterpolation(cfInterpolation(src,dst),cfInterpolation(src,dst));
0700 }
0701 
0702 
0703 template<class T>
0704 inline T cfPenumbraB(T src, T dst) {
0705     using namespace Arithmetic;
0706 
0707     if (dst == unitValue<T>()) {
0708         return unitValue<T>();
0709     }    
0710     if (dst + src < unitValue<T>()) {
0711         return (cfColorDodge(dst,src)/2);
0712     }
0713     if (src == zeroValue<T>()) {
0714         return zeroValue<T>();
0715     }
0716 
0717     return inv(clamp<T>(div(inv(dst),src)/2));
0718 }
0719 
0720 template<class T>
0721 inline T cfPenumbraD(T src, T dst) {
0722     using namespace Arithmetic;
0723     
0724     if (dst == unitValue<T>()) {
0725         return unitValue<T>();
0726     }
0727     
0728     return cfArcTangent(src,inv(dst));
0729 }
0730 
0731 template<class T>
0732 inline T cfPenumbraC(T src, T dst) {
0733     using namespace Arithmetic;
0734     
0735     return cfPenumbraD(dst,src);
0736 }
0737 
0738 template<class T>
0739 inline T cfPenumbraA(T src, T dst) {
0740     using namespace Arithmetic;
0741 
0742     return (cfPenumbraB(dst,src)); 
0743 }
0744 
0745 template<class T>
0746 inline T cfSoftLightIFSIllusions(T src, T dst) {
0747     using namespace Arithmetic;
0748     
0749     qreal fsrc = scale<qreal>(src);
0750     qreal fdst = scale<qreal>(dst);
0751     
0752     return scale<T>(pow(fdst,pow(2.0,(mul(2.0,.5f-fsrc))))); 
0753 }
0754 
0755 template<class T>
0756 inline T cfSoftLightPegtopDelphi(T src, T dst) {
0757     using namespace Arithmetic;
0758 
0759     return clamp<T>(cfAddition(mul(dst,cfScreen(src,dst)),mul(mul(src,dst),inv(dst)))); 
0760 }
0761 
0762 template<class T>
0763 inline T cfNegation(T src, T dst) {
0764     using namespace Arithmetic;
0765     typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
0766         
0767     composite_type unit = unitValue<T>();
0768     composite_type a = unit - src - dst;
0769     composite_type s = std::abs(a);
0770     composite_type d = unit - s;
0771         
0772     return T(d);
0773 }
0774 
0775 template<class T>
0776 inline T cfNor(T src, T dst) {
0777     using namespace Arithmetic;
0778         
0779     return and(src,dst);
0780 }
0781 
0782 template<class T>
0783 inline T cfNand(T src, T dst) {
0784     using namespace Arithmetic;
0785         
0786     return or(src,dst);
0787 }
0788 
0789 template<class T>
0790 inline T cfXor(T src, T dst) {
0791     using namespace Arithmetic;
0792     
0793     return xor(src,dst);
0794 }
0795 
0796 template<class T>
0797 inline T cfXnor(T src, T dst) {
0798     using namespace Arithmetic;
0799     
0800     return cfXor(src,inv(dst));
0801 }
0802 
0803 template<class T>
0804 inline T cfAnd(T src, T dst) {
0805     using namespace Arithmetic;
0806         
0807     return cfNor(inv(src),inv(dst));
0808 }
0809 
0810 template<class T>
0811 inline T cfOr(T src, T dst) {
0812     using namespace Arithmetic;
0813         
0814     return cfNand(inv(src),inv(dst));
0815 }
0816 
0817 template<class T>
0818 inline T cfConverse(T src, T dst) {
0819     using namespace Arithmetic;
0820         
0821     return cfOr(inv(src),dst);
0822 }
0823 
0824 template<class T>
0825 inline T cfNotConverse(T src, T dst) {
0826     using namespace Arithmetic;
0827         
0828     return cfAnd(src,inv(dst));
0829 }
0830 
0831 template<class T>
0832 inline T cfImplies(T src, T dst) {
0833     using namespace Arithmetic;
0834         
0835     return cfOr(src,inv(dst));
0836 }
0837 
0838 template<class T>
0839 inline T cfNotImplies(T src, T dst) {
0840     using namespace Arithmetic;
0841         
0842     return cfAnd(inv(src),dst);
0843 }
0844 
0845 template<class T>
0846 inline T cfPNormA(T src, T dst) {
0847     using namespace Arithmetic;
0848     //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...
0849     
0850     return clamp<T>(pow(pow((float)dst, 2.3333333333333333) + pow((float)src, 2.3333333333333333), 0.428571428571434)); 
0851 }
0852 
0853 template<class T>
0854 inline T cfPNormB(T src, T dst) {
0855     using namespace Arithmetic;
0856     //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...
0857     
0858     return clamp<T>(pow(pow(dst,4)+pow(src,4),0.25)); 
0859 }
0860 
0861 template<class T>
0862 inline T cfSuperLight(T src, T dst) {
0863     using namespace Arithmetic;
0864     //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.
0865         
0866     qreal fsrc = scale<qreal>(src);
0867     qreal fdst = scale<qreal>(dst);
0868     
0869     if (fsrc < .5) {
0870         return scale<T>(inv(pow(pow(inv(fdst),2.875)+pow(inv(2.0*fsrc),2.875),1.0/2.875)));
0871     }   
0872     
0873     return scale<T>(pow(pow(fdst,2.875)+pow(2.0*fsrc-1.0,2.875),1.0/2.875)); 
0874 }
0875 
0876 template<class T>
0877 inline T cfTintIFSIllusions(T src, T dst) {
0878     using namespace Arithmetic;
0879     //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.
0880     
0881     qreal fsrc = scale<qreal>(src);
0882     qreal fdst = scale<qreal>(dst);
0883     
0884     return scale<T>(fsrc*inv(fdst)+sqrt(fdst)); 
0885 }
0886 
0887 template<class T>
0888 inline T cfShadeIFSIllusions(T src, T dst) {
0889     using namespace Arithmetic;
0890     //Known as Shadow Blending mode found in IFS Illusions. Picked this name because it is the opposite of Tint (IFS Illusion Blending mode).
0891     
0892     qreal fsrc = scale<qreal>(src);
0893     qreal fdst = scale<qreal>(dst);
0894     
0895     return scale<T>(inv((inv(fdst)*fsrc)+sqrt(inv(fsrc)))); 
0896 }
0897 
0898 template<class T>
0899 inline T cfFogLightenIFSIllusions(T src, T dst) {
0900     using namespace Arithmetic;
0901     //Known as Bright Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradient.
0902     
0903     qreal fsrc = scale<qreal>(src);
0904     qreal fdst = scale<qreal>(dst);
0905     
0906     if (fsrc < .5) {
0907         return scale<T>(inv(inv(fsrc)*fsrc)-inv(fdst)*inv(fsrc));
0908     }  
0909     
0910     return scale<T>(fsrc-inv(fdst)*inv(fsrc)+pow(inv(fsrc),2)); 
0911 }
0912 
0913 template<class T>
0914 inline T cfFogDarkenIFSIllusions(T src, T dst) {
0915     using namespace Arithmetic;
0916     //Known as Dark Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradient.
0917     
0918     qreal fsrc = scale<qreal>(src);
0919     qreal fdst = scale<qreal>(dst);
0920     
0921     if (fsrc < .5) {
0922         return scale<T>(inv(fsrc)*fsrc+fsrc*fdst);
0923     }  
0924     
0925     return scale<T>(fsrc*fdst+fsrc-pow(fsrc,2)); 
0926 }
0927 
0928 template<class T>
0929 inline T cfModulo(T src, T dst) {
0930     using namespace Arithmetic;
0931     
0932     return mod(dst,src); 
0933 }
0934 
0935 template<class T>
0936 inline T cfModuloShift(T src, T dst) {
0937     using namespace Arithmetic;
0938     qreal fsrc = scale<qreal>(src);
0939     qreal fdst = scale<qreal>(dst);
0940     
0941     if (fsrc == 1.0 && fdst == 0.0) {
0942     return scale<T>(0.0);
0943     }  
0944 
0945     
0946     return scale<T>(mod((fdst+fsrc),1.0000000000)); 
0947 }
0948 
0949 template<class T>
0950 inline T cfModuloShiftContinuous(T src, T dst) {
0951     using namespace Arithmetic;
0952     //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.
0953     qreal fsrc = scale<qreal>(src);
0954     qreal fdst = scale<qreal>(dst);
0955     
0956     if (fsrc == 1.0 && fdst == 0.0) {
0957     return scale<T>(1.0);
0958     }  
0959     
0960     return scale<T>((int(ceil(fdst+fsrc)) % 2 != 0) || (fdst == zeroValue<T>()) ? cfModuloShift(fsrc,fdst) : inv(cfModuloShift(fsrc,fdst))); 
0961 }
0962 
0963 template<class T>
0964 inline T cfDivisiveModulo(T src, T dst) {
0965     using namespace Arithmetic;
0966     //I have to use 1.00000 as unitValue failed to work for those area.
0967     
0968     qreal fsrc = scale<qreal>(src);
0969     qreal fdst = scale<qreal>(dst);
0970     
0971         if (fsrc == zeroValue<T>()) {
0972         return scale<T>(mod(((1.0000000000/epsilon<T>()) * fdst),1.0000000000));
0973     }  
0974     
0975     return scale<T>(mod(((1.0000000000/fsrc) * fdst),1.0000000000)); 
0976 }
0977 
0978 template<class T>
0979 inline T cfDivisiveModuloContinuous(T src, T dst) {
0980     using namespace Arithmetic;
0981     
0982     qreal fsrc = scale<qreal>(src);
0983     qreal fdst = scale<qreal>(dst);
0984     
0985     if (fdst == zeroValue<T>()) {
0986     return zeroValue<T>();
0987     }  
0988     
0989     if (fsrc == zeroValue<T>()) {
0990     return cfDivisiveModulo(fsrc,fdst);
0991     }  
0992 
0993     
0994     return scale<T>( int(ceil(fdst/fsrc)) % 2 != 0 ? cfDivisiveModulo(fsrc,fdst) : inv(cfDivisiveModulo(fsrc,fdst))); 
0995 }
0996 
0997 template<class T>
0998 inline T cfModuloContinuous(T src, T dst) {
0999     using namespace Arithmetic;
1000     
1001     return cfMultiply(cfDivisiveModuloContinuous(src,dst),src); 
1002 }
1003 
1004 template<class T>
1005 inline T cfEasyDodge(T src, T dst) {
1006     using namespace Arithmetic;
1007     // The 13 divided by 15 can be adjusted to taste. See imgblend.m
1008     
1009     qreal fsrc = scale<qreal>(src);
1010     qreal fdst = scale<qreal>(dst);
1011     
1012     if (fsrc == 1.0) {
1013         return scale<T>(1.0);}
1014 
1015     
1016     return scale<T>(pow(fdst,mul(inv(fsrc != 1.0 ? fsrc : .999999999999),1.039999999))); 
1017 }
1018 
1019 template<class T>
1020 inline T cfEasyBurn(T src, T dst) {
1021     using namespace Arithmetic;
1022     // The 13 divided by 15 can be adjusted to taste. See imgblend.m
1023     
1024     qreal fsrc = scale<qreal>(src);
1025     qreal fdst = scale<qreal>(dst);
1026 
1027     
1028     return scale<T>(inv(pow(inv(fsrc != 1.0 ? fsrc : .999999999999),mul(fdst,1.039999999)))); 
1029 }
1030 
1031 template<class T>
1032 inline T cfFlatLight(T src, T dst) {
1033     using namespace Arithmetic;
1034     
1035     if (src == zeroValue<T>()) {
1036     return zeroValue<T>();
1037     }  
1038     
1039     return clamp<T>(cfHardMixPhotoshop(inv(src),dst)==unitValue<T>() ? cfPenumbraB(src,dst) : cfPenumbraA(src,dst)); 
1040 }
1041 
1042 
1043 template<class HSXType, class TReal>
1044 inline void cfAdditionSAI(TReal src, TReal sa, TReal& dst, TReal& da)
1045 {
1046     using namespace Arithmetic;
1047     typedef typename KoColorSpaceMathsTraits<TReal>::compositetype composite_type;
1048 
1049     Q_UNUSED(da);
1050     composite_type newsrc;
1051     newsrc = mul(src, sa);
1052     dst = clamp<TReal>(newsrc + dst);
1053 }
1054 
1055 
1056 
1057 #endif // KOCOMPOSITEOP_FUNCTIONS_H_