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_