KoCompositeOpFunctions.h 26.6 KB
Newer Older
1 2 3 4 5 6
/*
 *  Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

20 21
#ifndef KOCOMPOSITEOP_FUNCTIONS_H_
#define KOCOMPOSITEOP_FUNCTIONS_H_
22 23 24

#include <KoColorSpaceMaths.h>

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
template<class HSXType, class TReal>
inline void cfReorientedNormalMapCombine(TReal srcR, TReal srcG, TReal srcB, TReal& dstR, TReal& dstG, TReal& dstB)
{
    // see http://blog.selfshadow.com/publications/blending-in-detail/ by Barre-Brisebois and Hill
    TReal tx = 2*srcR-1;
    TReal ty = 2*srcG-1;
    TReal tz = 2*srcB;
    TReal ux = -2*dstR+1;
    TReal uy = -2*dstG+1;
    TReal uz = 2*dstB-1;
    TReal k = (tx*ux+ty*uy+tz*uz)/tz; // dot(t,u)/t.z
    TReal rx = tx*k-ux;
    TReal ry = ty*k-uy;
    TReal rz = tz*k-uz;
    k = 1/sqrt(rx*rx+ry*ry+rz*rz); // normalize result
    rx *= k;
    ry *= k;
    rz *= k;
    dstR = rx*0.5+0.5;
    dstG = ry*0.5+0.5;
    dstB = rz*0.5+0.5;
}

48
template<class HSXType, class TReal>
49
inline void cfColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
50
    TReal lum = getLightness<HSXType>(dr, dg, db);
51 52 53
    dr = sr;
    dg = sg;
    db = sb;
54
    setLightness<HSXType>(dr, dg, db, lum);
55 56
}

57 58 59
template<class HSXType, class TReal>
inline void cfLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
    setLightness<HSXType>(dr, dg, db, getLightness<HSXType>(sr, sg, sb));
60 61
}

62 63 64 65 66 67 68
template<class HSXType, class TReal>
inline void cfIncreaseLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
    addLightness<HSXType>(dr, dg, db, getLightness<HSXType>(sr, sg, sb));
}

template<class HSXType, class TReal>
inline void cfDecreaseLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
69
    addLightness<HSXType>(dr, dg, db, getLightness<HSXType>(sr, sg, sb) - TReal(1.0));
70 71
}

72
template<class HSXType, class TReal>
73
inline void cfSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
74 75
    TReal sat   = getSaturation<HSXType>(sr, sg, sb);
    TReal light = getLightness<HSXType>(dr, dg, db);
76
    setSaturation<HSXType>(dr, dg, db, sat);
77 78 79 80 81 82
    setLightness<HSXType>(dr, dg, db, light);
}

template<class HSXType, class TReal>
inline void cfIncreaseSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
    using namespace Arithmetic;
83
    TReal sat   = lerp(getSaturation<HSXType>(dr,dg,db), unitValue<TReal>(), getSaturation<HSXType>(sr,sg,sb));
84 85 86 87 88 89 90 91
    TReal light = getLightness<HSXType>(dr, dg, db);
    setSaturation<HSXType>(dr, dg, db, sat);
    setLightness<HSXType>(dr, dg, db, light);
}

template<class HSXType, class TReal>
inline void cfDecreaseSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
    using namespace Arithmetic;
92
    TReal sat   = lerp(zeroValue<TReal>(), getSaturation<HSXType>(dr,dg,db), getSaturation<HSXType>(sr,sg,sb));
93 94 95
    TReal light = getLightness<HSXType>(dr, dg, db);
    setSaturation<HSXType>(dr, dg, db, sat);
    setLightness<HSXType>(dr, dg, db, light);
96 97
}

98
template<class HSXType, class TReal>
99
inline void cfHue(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
100 101
    TReal sat = getSaturation<HSXType>(dr, dg, db);
    TReal lum = getLightness<HSXType>(dr, dg, db);
102 103 104
    dr = sr;
    dg = sg;
    db = sb;
105 106
    setSaturation<HSXType>(dr, dg, db, sat);
    setLightness<HSXType>(dr, dg, db, lum);
107 108
}

109 110 111 112 113 114 115 116
template<class HSXType, class TReal>
inline void cfTangentNormalmap(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
    using namespace Arithmetic;
    TReal half=halfValue<TReal>();
    
    dr = sr+(dr-half);
    dg = sg+(dg-half);
    db = sb+(db-unitValue<TReal>());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
117
} 
118
    
119 120 121 122 123 124
template<class HSXType, class TReal>
inline void cfDarkerColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
    
    TReal lum = getLightness<HSXType>(dr, dg, db);
    TReal lum2 = getLightness<HSXType>(sr, sg, sb);
    if (lum<lum2) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
125 126 127
        sr = dr;
        sg = dg;
        sb = db;
128 129
    }
    else {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
130 131 132
        dr = sr;
        dg = sg;
        db = sb;
133 134 135 136 137 138
    }

}

template<class HSXType, class TReal>
inline void cfLighterColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
139
    
140 141 142
    TReal lum = getLightness<HSXType>(dr, dg, db);
    TReal lum2 = getLightness<HSXType>(sr, sg, sb);
    if (lum>lum2) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
143 144 145
        sr = dr;
        sg = dg;
        sb = db;
146 147
    }
    else {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
148 149 150
        dr = sr;
        dg = sg;
        db = sb;
151
    }
152 153
}

154 155
template<class T>
inline T cfColorBurn(T src, T dst) {
156 157
    using namespace Arithmetic;
    
158 159
    if(dst == unitValue<T>())
        return unitValue<T>();
160 161 162 163
    
    T invDst = inv(dst);
    
    if(src < invDst)
164
        return zeroValue<T>();
165 166
    
    return inv(clamp<T>(div(invDst, src)));
167 168 169
}

template<class T>
170
inline T cfLinearBurn(T src, T dst) {
171
    using namespace Arithmetic;
172
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
173
    return clamp<T>(composite_type(src) + dst - unitValue<T>());
174 175 176 177
}

template<class T>
inline T cfColorDodge(T src, T dst) {
178
    using namespace Arithmetic;
179 180 181 182
    //Fixing Color Dodge to avoid ZX Colors on bright area.

    if(src == unitValue<T>())
        return unitValue<T>();
183 184 185
    
    T invSrc = inv(src);
    
186
    if(invSrc == zeroValue<T>())
187
        return unitValue<T>();
188
    
189
    return Arithmetic::clamp<T>(div(dst, invSrc));
190 191
}

192 193 194
template<class T>
inline T cfAddition(T src, T dst) {
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
195
    return Arithmetic::clamp<T>(composite_type(src) + dst);
196 197 198 199
}

template<class T>
inline T cfSubtract(T src, T dst) {
200 201 202 203 204 205 206 207
    using namespace Arithmetic;
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
    return clamp<T>(composite_type(dst) - src);
}

template<class T>
inline T cfInverseSubtract(T src, T dst) {
    using namespace Arithmetic;
208
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
209
    return clamp<T>(composite_type(dst) - inv(src));
210 211 212 213
}

template<class T>
inline T cfExclusion(T src, T dst) {
214
    using namespace Arithmetic;
215
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
216
    
217 218 219 220 221 222
    composite_type x = mul(src, dst);
    return clamp<T>(composite_type(dst) + src - (x + x));
}

template<class T>
inline T cfDivide(T src, T dst) {
223
    using namespace Arithmetic;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
224
    //typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
225
    
226 227
    if(src == zeroValue<T>())
        return (dst == zeroValue<T>()) ? zeroValue<T>() : unitValue<T>();
228
    
229 230 231
    return clamp<T>(div(dst, src));
}

232 233
template<class T>
inline T cfHardLight(T src, T dst) {
234
    using namespace Arithmetic;
235
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
236
    
237 238
    composite_type src2 = composite_type(src) + src;
    
239
    if(src > halfValue<T>()) {
240
        // screen(src*2.0 - 1.0, dst)
241
        src2 -= unitValue<T>();
242 243 244

        // src2 is guaranteed to be smaller than unitValue<T>() now
        return Arithmetic::unionShapeOpacity(T(src2), dst);
245 246
    }
    
247 248
    // src2 is guaranteed to be smaller than unitValue<T>() due to 'if'
    return Arithmetic::mul(T(src2), dst);
249 250
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
template<class T>
inline T cfSoftLightSvg(T src, T dst) {
    using namespace Arithmetic;

    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);

    if(fsrc > 0.5f) {
        qreal D = (fdst > 0.25f) ? sqrt(fdst) : ((16.0f*fdst - 12.0)*fdst + 4.0f)*fdst;
        return scale<T>(fdst + (2.0f*fsrc - 1.0f) * (D - fdst));
    }

    return scale<T>(fdst - (1.0f - 2.0f * fsrc) * fdst * (1.0f - fdst));
}


267 268
template<class T>
inline T cfSoftLight(T src, T dst) {
269 270
    using namespace Arithmetic;
    
271 272 273 274
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if(fsrc > 0.5f) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
275
        return scale<T>(fdst + (2.0f * fsrc - 1.0f) * (sqrt(fdst) - fdst));
276 277 278 279 280
    }
    
    return scale<T>(fdst - (1.0f - 2.0f*fsrc) * fdst * (1.0f - fdst));
}

281 282
template<class T>
inline T cfVividLight(T src, T dst) {
283
    using namespace Arithmetic;
284 285
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
    
286 287 288
    if(src < halfValue<T>()) {
        if(src == zeroValue<T>())
            return (dst == unitValue<T>()) ? unitValue<T>() : zeroValue<T>();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
289

290 291 292
        // min(1,max(0,1-(1-dst) / (2*src)))
        composite_type src2 = composite_type(src) + src;
        composite_type dsti = inv(dst);
293
        return clamp<T>(unitValue<T>() - (dsti * unitValue<T>() / src2));
294 295
    }
    
296 297
    if(src == unitValue<T>())
        return (dst == zeroValue<T>()) ? zeroValue<T>() : unitValue<T>();
298 299 300 301
    
    // min(1,max(0, dst / (2*(1-src)))
    composite_type srci2 = inv(src);
    srci2 += srci2;
302
    return clamp<T>(composite_type(dst) * unitValue<T>() / srci2);
303 304 305 306 307
}

template<class T>
inline T cfPinLight(T src, T dst) {
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
308
    // TODO: verify that the formula is correct (the first max would be useless here)
309 310 311
    // max(0, max(2*src-1, min(dst, 2*src)))
    composite_type src2 = composite_type(src) + src;
    composite_type a    = qMin<composite_type>(dst, src2);
312
    composite_type b    = qMax<composite_type>(src2-Arithmetic::unitValue<T>(), a);
313 314 315 316 317
    return T(b);
}

template<class T>
inline T cfArcTangent(T src, T dst) {
318
    using namespace Arithmetic;
319
    
320 321
    if(dst == zeroValue<T>())
        return (src == zeroValue<T>()) ? zeroValue<T>() : unitValue<T>();
322
    
323
    return scale<T>(2.0 * atan(scale<qreal>(src) / scale<qreal>(dst)) / Arithmetic::pi);
324 325
}

326 327
template<class T>
inline T cfAllanon(T src, T dst) {
328
    using namespace Arithmetic;
329 330
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
    // (dst + src) / 2   [or (dst + src) * 0.5]
331
    return T((composite_type(src) + dst) * halfValue<T>() / unitValue<T>());
332 333 334 335
}

template<class T>
inline T cfLinearLight(T src, T dst) {
336
    using namespace Arithmetic;
337
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
338
    
339
    // min(1,max(0,(dst + 2*src)-1))
340
    return clamp<T>((composite_type(src) + src + dst) - unitValue<T>());
341 342 343 344
}

template<class T>
inline T cfParallel(T src, T dst) {
345
    using namespace Arithmetic;
346 347 348
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
    
    // min(max(2 / (1/dst + 1/src), 0), 1)
349 350
    composite_type unit = unitValue<T>();
    composite_type s    = (src != zeroValue<T>()) ? div<T>(unit, src) : unit;
351 352 353 354 355 356 357 358 359
    composite_type d    = (dst != zeroValue<T>()) ? div<T>(unit, dst) : unit;    
    if (src == zeroValue<T>()) {
        return zeroValue<T>();    
    }

    if (dst == zeroValue<T>()) {
        return zeroValue<T>();    
    }

360
    return clamp<T>((unit+unit) * unit / (d+s));
361 362 363 364 365 366 367
}

template<class T>
inline T cfEquivalence(T src, T dst) {
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
    // 1 - abs(dst - src)
    composite_type x = composite_type(dst) - src;
368
    return (x < Arithmetic::zeroValue<T>()) ? T(-x) : T(x);
369 370
}

371 372
template<class T>
inline T cfGrainMerge(T src, T dst) {
373
    using namespace Arithmetic;
374
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
375
    return clamp<T>(composite_type(dst) + src - halfValue<T>());
376 377 378 379
}

template<class T>
inline T cfGrainExtract(T src, T dst) {
380
    using namespace Arithmetic;
381
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
382
    return clamp<T>(composite_type(dst) - src + halfValue<T>());
383 384 385 386
}

template<class T>
inline T cfHardMix(T src, T dst) {
387
    return (dst > Arithmetic::halfValue<T>()) ? cfColorDodge(src,dst) : cfColorBurn(src,dst);
388 389
}

390 391 392 393 394 395 396 397 398 399
template<class T>
inline T cfHardMixPhotoshop(T src, T dst) {
    using namespace Arithmetic;
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;

    const composite_type sum = composite_type(src) + dst;

    return sum > unitValue<T>() ? unitValue<T>() : zeroValue<T>();
}

400
template<class T>
401
inline T cfAdditiveSubtractive(T src, T dst) {
402
    using namespace Arithmetic;
403 404 405 406 407
    // min(1,max(0,abs(sqr(CB)-sqr(CT))))
    qreal x = sqrt(scale<qreal>(dst)) - sqrt(scale<qreal>(src));
    return scale<T>((x < 0.0) ? -x : x);
}

408 409
template<class T>
inline T cfGammaDark(T src, T dst) {
410 411
    using namespace Arithmetic;
    
412 413
    if(src == zeroValue<T>())
        return zeroValue<T>();
414 415 416 417 418 419
    
    // power(dst, 1/src)
    return scale<T>(pow(scale<qreal>(dst), 1.0/scale<qreal>(src)));
}

template<class T>
420 421 422 423
inline T cfGammaLight(T src, T dst) {
    using namespace Arithmetic;
    return scale<T>(pow(scale<qreal>(dst), scale<qreal>(src)));
}
424

425 426 427 428 429 430
template<class T>
inline T cfGammaIllumination(T src, T dst) {
    using namespace Arithmetic;
    return inv(cfGammaDark(inv(src),inv(dst)));
}

431
template<class T>
432 433 434 435
inline T cfGeometricMean(T src, T dst) {
    using namespace Arithmetic;
    return scale<T>(sqrt(scale<qreal>(dst) * scale<qreal>(src)));
}
436

437 438 439
template<class T>
inline T cfOver(T src, T dst) { Q_UNUSED(dst); return src; }

440 441 442
template<class T>
inline T cfOverlay(T src, T dst) { return cfHardLight(dst, src); }

443
template<class T>
444
inline T cfMultiply(T src, T dst) { return Arithmetic::mul(src, dst); }
445

446 447 448 449 450 451
template<class T>
inline T cfHardOverlay(T src, T dst) {
    using namespace Arithmetic;

    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
452 453 454
    
    if (fsrc == 1.0) {
        return scale<T>(1.0);}
455 456 457 458 459 460 461

    if(fsrc > 0.5f) {
        return scale<T>(cfDivide(inv(2.0 * fsrc - 1.0f), fdst));
    }
    return scale<T>(mul(2.0 * fsrc, fdst));
}

462 463 464 465
template<class T>
inline T cfDifference(T src, T dst) { return qMax(src,dst) - qMin(src,dst); }

template<class T>
466
inline T cfScreen(T src, T dst) { return Arithmetic::unionShapeOpacity(src, dst); }
467 468 469 470 471 472 473

template<class T>
inline T cfDarkenOnly(T src, T dst) { return qMin(src, dst); }

template<class T>
inline T cfLightenOnly(T src, T dst) { return qMax(src, dst); }

Miguel Lopez's avatar
Miguel Lopez committed
474 475 476 477
template<class T>
inline T cfGlow(T src, T dst) {
    using namespace Arithmetic;
        // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
478 479 480 481 482 483 484 485 486 487 488

    if (dst == unitValue<T>()) {
        return unitValue<T>();
    }

    return clamp<T>(div(mul(src, src), inv(dst)));
}

template<class T>
inline T cfReflect(T src, T dst) {
    using namespace Arithmetic;
Miguel Lopez's avatar
Miguel Lopez committed
489
    
490 491 492 493 494 495 496 497 498
    
    return clamp<T>(cfGlow(dst,src));
}

template<class T>
inline T cfHeat(T src, T dst) {
    using namespace Arithmetic;
    
    if(src == unitValue<T>()) {
Miguel Lopez's avatar
Miguel Lopez committed
499 500 501
        return unitValue<T>();
    }

502
    if(dst == zeroValue<T>()) {
Miguel Lopez's avatar
Miguel Lopez committed
503 504
        return zeroValue<T>();
    }
505 506 507 508 509 510 511

    return inv(clamp<T>(div(mul(inv(src), inv(src)),dst)));
}

template<class T>
inline T cfFreeze(T src, T dst) {
    using namespace Arithmetic;
Miguel Lopez's avatar
Miguel Lopez committed
512
    
513
    return (cfHeat(dst,src)); 
Miguel Lopez's avatar
Miguel Lopez committed
514
}
515 516 517 518 519

template<class T>
inline T cfHelow(T src, T dst) {
    using namespace Arithmetic;
    // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
Miguel Lopez's avatar
Miguel Lopez committed
520
    
521 522 523 524 525 526 527 528 529 530 531
    if (cfHardMixPhotoshop(src,dst) == unitValue<T>()) {
        return cfHeat(src,dst);
    }
    
    if (src == zeroValue<T>()) {
        return zeroValue<T>();
    }

    return (cfGlow(src,dst));
}

Miguel Lopez's avatar
Miguel Lopez committed
532
template<class T>
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
inline T cfFrect(T src, T dst) {
    using namespace Arithmetic;

    if (cfHardMixPhotoshop(src,dst) == unitValue<T>()) {
        return cfFreeze(src,dst);
    }

    if (dst == zeroValue<T>()) {
        return zeroValue<T>();
    }

    return (cfReflect(src,dst));
}

template<class T>
inline T cfGleat(T src, T dst) {
Miguel Lopez's avatar
Miguel Lopez committed
549
    using namespace Arithmetic;
550 551 552 553 554
        // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
    
    if(dst == unitValue<T>()) {
        return unitValue<T>();
    }    
Miguel Lopez's avatar
Miguel Lopez committed
555
    
556 557 558 559 560
    if(cfHardMixPhotoshop(src,dst) == unitValue<T>()) {
        return cfGlow(src,dst);
    }
    
    return (cfHeat(src,dst));
Miguel Lopez's avatar
Miguel Lopez committed
561 562 563
}

template<class T>
564
inline T cfReeze(T src, T dst) {
Miguel Lopez's avatar
Miguel Lopez committed
565 566
    using namespace Arithmetic;
    
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
    return (cfGleat(dst,src)); 
}
template<class T>
inline T cfFhyrd(T src, T dst) {
    using namespace Arithmetic;
    
    return (cfAllanon(cfFrect(src,dst),cfHelow(src,dst))); 
}

template<class T>
inline T cfInterpolation(T src, T dst) {
    using namespace Arithmetic;

    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if(dst == zeroValue<T>() && src == zeroValue<T>()) {
Miguel Lopez's avatar
Miguel Lopez committed
584
        return zeroValue<T>();
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
    } 

    return scale<T>(.5f-.25f*cos(pi*(fsrc))-.25f*cos(pi*(fdst)));
}

template<class T>
inline T cfInterpolationB(T src, T dst) {
    using namespace Arithmetic;

    return cfInterpolation(cfInterpolation(src,dst),cfInterpolation(src,dst));
}


template<class T>
inline T cfPenumbraB(T src, T dst) {
    using namespace Arithmetic;

    if (dst == unitValue<T>()) {
        return unitValue<T>();
    }    
    if (dst + src < unitValue<T>()) {
        return (cfColorDodge(dst,src)/2);
Miguel Lopez's avatar
Miguel Lopez committed
607
    }
608 609 610 611 612 613 614 615 616 617
    if (src == zeroValue<T>()) {
        return zeroValue<T>();
    }

    return inv(clamp<T>(div(inv(dst),src)/2));
}

template<class T>
inline T cfPenumbraD(T src, T dst) {
    using namespace Arithmetic;
Miguel Lopez's avatar
Miguel Lopez committed
618
    
619
    if (dst == unitValue<T>()) {
Miguel Lopez's avatar
Miguel Lopez committed
620 621 622
        return unitValue<T>();
    }
    
623
    return cfArcTangent(src,inv(dst));
Miguel Lopez's avatar
Miguel Lopez committed
624 625 626
}

template<class T>
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
inline T cfPenumbraC(T src, T dst) {
    using namespace Arithmetic;
    
    return cfPenumbraD(dst,src);
}

template<class T>
inline T cfPenumbraA(T src, T dst) {
    using namespace Arithmetic;

    return (cfPenumbraB(dst,src)); 
}

template<class T>
inline T cfSoftLightIFSIllusions(T src, T dst) {
    using namespace Arithmetic;
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    return scale<T>(pow(fdst,pow(2.0,(mul(2.0,.5f-fsrc))))); 
}

template<class T>
inline T cfSoftLightPegtopDelphi(T src, T dst) {
    using namespace Arithmetic;

    return clamp<T>(cfAddition(mul(dst,cfScreen(src,dst)),mul(mul(src,dst),inv(dst)))); 
}

template<class T>
inline T cfNegation(T src, T dst) {
    using namespace Arithmetic;
    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
        
    composite_type unit = unitValue<T>();
    composite_type a = unit - src - dst;
    composite_type s = abs(a);
    composite_type d = unit - s;
        
    return T(d);
}

template<class T>
inline T cfNor(T src, T dst) {
    using namespace Arithmetic;
        
    return and(src,dst);
}

template<class T>
inline T cfNand(T src, T dst) {
    using namespace Arithmetic;
        
    return or(src,dst);
}

template<class T>
inline T cfXor(T src, T dst) {
    using namespace Arithmetic;
    
    return xor(src,dst);
}

template<class T>
inline T cfXnor(T src, T dst) {
    using namespace Arithmetic;
    
    return cfXor(src,inv(dst));
}

template<class T>
inline T cfAnd(T src, T dst) {
Miguel Lopez's avatar
Miguel Lopez committed
700
    using namespace Arithmetic;
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
        
    return cfNor(inv(src),inv(dst));
}

template<class T>
inline T cfOr(T src, T dst) {
    using namespace Arithmetic;
        
    return cfNand(inv(src),inv(dst));
}

template<class T>
inline T cfConverse(T src, T dst) {
    using namespace Arithmetic;
        
    return cfOr(inv(src),dst);
}

template<class T>
inline T cfNotConverse(T src, T dst) {
    using namespace Arithmetic;
        
    return cfAnd(src,inv(dst));
}

template<class T>
inline T cfImplies(T src, T dst) {
    using namespace Arithmetic;
        
    return cfOr(src,inv(dst));
}

template<class T>
inline T cfNotImplies(T src, T dst) {
    using namespace Arithmetic;
        
    return cfAnd(inv(src),dst);
}

template<class T>
inline T cfPNormA(T src, T dst) {
    using namespace Arithmetic;
    //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...
    
    return clamp<T>(pow(pow(dst,2.3333333333333333)+pow(src,2.3333333333333333),0.428571428571434)); 
}

template<class T>
inline T cfPNormB(T src, T dst) {
    using namespace Arithmetic;
    //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...
    
    return clamp<T>(pow(pow(dst,4)+pow(src,4),0.25)); 
}

template<class T>
inline T cfSuperLight(T src, T dst) {
    using namespace Arithmetic;
    //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.
        
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if (fsrc < .5) {
        return scale<T>(inv(pow(pow(inv(fdst),2.875)+pow(inv(2.0*fsrc),2.875),1.0/2.875)));
    }   
    
    return scale<T>(pow(pow(fdst,2.875)+pow(2.0*fsrc-1.0,2.875),1.0/2.875)); 
}

template<class T>
inline T cfTintIFSIllusions(T src, T dst) {
    using namespace Arithmetic;
    //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.
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    return scale<T>(fsrc*inv(fdst)+sqrt(fdst)); 
}

template<class T>
inline T cfShadeIFSIllusions(T src, T dst) {
    using namespace Arithmetic;
    //Known as Shadow Blending mode found in IFS Illusions. Picked this name because it is the opposite of Tint (IFS Illusion Blending mode).
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    return scale<T>(inv((inv(fdst)*fsrc)+sqrt(inv(fsrc)))); 
}

template<class T>
inline T cfFogLightenIFSIllusions(T src, T dst) {
    using namespace Arithmetic;
    //Known as Bright Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradientt.
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if (fsrc < .5) {
        return scale<T>(inv(inv(fsrc)*fsrc)-inv(fdst)*inv(fsrc));
    }  
    
    return scale<T>(fsrc-inv(fdst)*inv(fsrc)+pow(inv(fsrc),2)); 
}

template<class T>
inline T cfFogDarkenIFSIllusions(T src, T dst) {
    using namespace Arithmetic;
    //Known as Dark Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradient.
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if (fsrc < .5) {
        return scale<T>(inv(fsrc)*fsrc+fsrc*fdst);
    }  
    
    return scale<T>(fsrc*fdst+fsrc-pow(fsrc,2)); 
}

template<class T>
inline T cfModulo(T src, T dst) {
    using namespace Arithmetic;
    
    return mod(dst,src); 
}

template<class T>
inline T cfModuloShift(T src, T dst) {
    using namespace Arithmetic;
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if (fsrc == 1.0 && fdst == 0.0) {
    return scale<T>(0.0);
    }  

Miguel Lopez's avatar
Miguel Lopez committed
840
    
841
    return scale<T>(mod((fdst+fsrc),1.0000000000)); 
Miguel Lopez's avatar
Miguel Lopez committed
842 843
}

844 845 846 847 848 849 850 851 852 853 854 855 856
template<class T>
inline T cfModuloShiftContinuous(T src, T dst) {
    using namespace Arithmetic;
    //This blending mode do not behave like difference/equilavent 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.
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if (fsrc == 1.0 && fdst == 0.0) {
    return scale<T>(0.0);
    }  
    
    return scale<T>((int(ceil(fdst+fsrc)) % 2 != 0) || (fdst == zeroValue<T>()) ? inv(cfModuloShift(fsrc,fdst)) : cfModuloShift(fsrc,fdst)); 
}
Miguel Lopez's avatar
Miguel Lopez committed
857

858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
template<class T>
inline T cfDivisiveModulo(T src, T dst) {
    using namespace Arithmetic;
    //I have to use 1.00000 as unitValue failed to work for those area.
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
        if (fsrc == zeroValue<T>()) {
        return scale<T>(mod(((1.0000000000/epsilon<T>()) * fdst),1.0000000000));
    }  
    
    return scale<T>(mod(((1.0000000000/fsrc) * fdst),1.0000000000)); 
}

template<class T>
inline T cfDivisiveModuloContinuous(T src, T dst) {
    using namespace Arithmetic;
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if (fdst == zeroValue<T>()) {
    return zeroValue<T>();
    }  
    
    if (fsrc == zeroValue<T>()) {
    return cfDivisiveModulo(fsrc,fdst);
    }  

    
    return scale<T>( int(ceil(fdst/fsrc)) % 2 != 0 ? cfDivisiveModulo(fsrc,fdst) : inv(cfDivisiveModulo(fsrc,fdst))); 
}

template<class T>
inline T cfModuloContinuous(T src, T dst) {
    using namespace Arithmetic;
    
    return cfMultiply(cfDivisiveModuloContinuous(src,dst),src); 
}

template<class T>
inline T cfColorBurnLogarithmic(T src, T dst) {
    using namespace Arithmetic;
    //Also known as Darken from EffectBank/Illusions.hu. IFS Illusions had used this blending mode.
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if (inv(fdst) == zeroValue<T>()) {
    return scale<T>(log2(1.0 + abs(fsrc)/abs(inv(.999999))/8));
    }  
    
    return scale<T>(log2(1.0 + abs(fsrc)/abs(inv(fdst))/8)); 
}

template<class T>
inline T cfColorDodgeLogarithmic(T src, T dst) {
    using namespace Arithmetic;
    //Also known as Lighten from EffectBank/Illusions.hu. IFS Illusions had used this blending mode.
        
    return inv(cfColorBurnLogarithmic(inv(src),inv(dst))); 
}

template<class T>
inline T cfEasyDodge(T src, T dst) {
    using namespace Arithmetic;
    // The 13 divided by 15 can be adjusted to taste. See imgblend.m
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);
    
    if (fsrc == 1.0) {
        return scale<T>(1.0);}

    
    return scale<T>(pow(fdst,mul(inv(fsrc != 1.0 ? fsrc : .999999999999),1.039999999))); 
}

template<class T>
inline T cfEasyBurn(T src, T dst) {
    using namespace Arithmetic;
    // The 13 divided by 15 can be adjusted to taste. See imgblend.m
    
    qreal fsrc = scale<qreal>(src);
    qreal fdst = scale<qreal>(dst);

    
    return scale<T>(inv(pow(inv(fsrc != 1.0 ? fsrc : .999999999999),mul(fdst,1.039999999)))); 
}

template<class T>
inline T cfFlatLight(T src, T dst) {
    using namespace Arithmetic;
    
    if (src == zeroValue<T>()) {
    return zeroValue<T>();
    }  
    
    return clamp<T>(cfHardMixPhotoshop(inv(src),dst)==unitValue<T>() ? cfPenumbraB(src,dst) : cfPenumbraA(src,dst)); 
}
959
#endif // KOCOMPOSITEOP_FUNCTIONS_H_