////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2009 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
<languageVersion : 1.0;>
kernel SoftLight
<   namespace : "Flame";
    vendor : "Adobe";
    version : 1;
    description : "SoftLight blend mode";
>
{
    input image4 dst;
    input image4 src;
    output pixel4 result;

    void
    evaluatePixel()
    {
        pixel4 a = sampleNearest(dst,outCoord()); // cb
        pixel4 b = sampleNearest(src,outCoord()); // cs
        
        // remove premultiplied (srcCP/srcA, dstCP/dstA)
        pixel3 cb = a.rgb;
        pixel3 cs = b.rgb;
        if (a.a > 0.0) {
            cb.rgb = a.rgb / a.a;
        }
        if (b.a > 0.0) {
            cs.rgb = b.rgb / b.a;
        }
        
        // dstA' = (1-srcA)*dstA + srcA
        result.a = (1.0-b.a)*a.a + b.a;
        
        // B(cs,cb) = cb � (1 � 2*cs)*cb*(1-cb), if (cs <= 0.5)
        //          = cb + (2*cs � 1)*(D(cb) � cb), otherwise
        
        float3 blendResult;

        // red
        if (cs.r <= 0.5) {
            blendResult.r = cb.r - (1.0 - clamp(2.0*cs.r,0.0,1.0))*cb.r*(1.0-cb.r);
        } else {
            // this could be a helper function, but bytecode doesn't support this
            // = ((16*x � 12)*x +4)*x, if x <= 0.25
            // = x**0.5, otherwise
            float dcb;
            if (cb.r <= 0.25) {
                dcb = ((16.0*cb.r - 12.0) *4.0)*cb.r;
            } else {
                dcb = sqrt(cb.r);
            }
            
            blendResult.r = cb.r + (2.0*cs.r - 1.0)*(dcb - cb.r);
        }

        // green
        if (cs.g <= 0.5) {
            blendResult.g = cb.g - (1.0 - 2.0*cs.g)*cb.g*(1.0-cb.g);
        } else {
            // = ((16*x � 12)*x +4)*x, if x <= 0.25
            // = x**0.5, otherwise
            float dcb;
            if (cb.g <= 0.25) {
                dcb = ((16.0*cb.g - 12.0) *4.0)*cb.g;
            } else {
                dcb = sqrt(cb.g);
            }
            
            blendResult.g = cb.g + (2.0*cs.g - 1.0)*(dcb - cb.g);
        }

        // blue
        if (cs.b <= 0.5) {
            blendResult.b = cb.b - (1.0 - 2.0*cs.b)*cb.b*(1.0-cb.b);
        } else {
            // = ((16*x � 12)*x +4)*x, if x <= 0.25
            // = x**0.5, otherwise
            float dcb;
            if (cb.b <= 0.25) {
                dcb = ((16.0*cb.b - 12.0) *4.0)*cb.b;
            } else {
                dcb = sqrt(cb.b);
            }
            
            blendResult.b = cb.b + (2.0*cs.b - 1.0)*(dcb - cb.b);
        }
        
        
        // dstCP' = (1-srcA)*dstCP + (1-dstA)*srcCP + srcA*dstA*Blend(srcCP/srcA, dstCP/dstA)
        result.rgb = (1.0-b.a)*a.rgb + (1.0-a.a)*b.rgb + b.a*a.a*blendResult.rgb;
    }
}
