////////////////////////////////////////////////////////////////////////////////
//
//  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 Hue
<   namespace : "Flame";
    vendor : "Adobe";
    version : 1;
    description : "Hue blend mode";
>
{
    input image4 dst;
    input image4 src;
    output pixel4 result;

    // PB bytecode can't do functions, use macros to define common functions
    // gets the max number out of the three
    #define max3( x, y, z ) ( max((x), max((y), (z))) )
    // gets the min number out of the three
    #define min3( x, y, z ) ( min((x), min((y), (z))) )

    // gets the max number out of the three elements in a vector
    #define max3v(C) ( max3((C.x), (C.y), (C.z)) )
    // gets the min number out of the three elements in a vector
    #define min3v(C) ( min3((C.x), (C.y), (C.z)) )

    // Sat - returns float, takes in a pixel3, or pixel4
    #define saturation(C) ( (max3((C.r), (C.g), (C.b)) - min3((C.r), (C.g), (C.b))) )

    // Luminance - returns float, takes in a pixel3, or pixel4
    #define luminance(C) ( (((C.r) * 0.3) + ((C.g) * 0.59) + ((C.b) * 0.11)) )

    void
    evaluatePixel()
    {
        pixel4 a = sampleNearest(dst,outCoord()); // cb
        pixel4 b = sampleNearest(src,outCoord()); // cs
        
        // remove premultiplied (srcCP/srcA, dstCP/dstA)
        pixel3 cb, cs;
        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;
        
        
        // setSat(cs, Sat(cb))
        
        // setSat (setSatColor, sat) -> setSatResult
        /* --------------------------------------------------------
         * void setsaturation(inout float3 color, in float satVal)
         *     makes color have the target saturation.
         * input and output of float3 color
         * input of the target saturation
         *
         * --------------------------------------------------------
         void setsatcomponents(inout float minComp, inout float midComp, inout float maxComp, in float satVal)
         {
             midComp -= minComp;
             maxComp -= minComp;
             minComp = 0.0;
             if (maxComp > 0.0) {
                 // max(..., 0.0000001) prevents divide by 0
                 midComp *= satVal/max(maxComp, 0.0000001);
                 maxComp = satVal;
             }
         }
         */

        float3 color = cs.rgb;
        float satVal = saturation(cb);

        if (color.x <= color.y) {
            if (color.y <= color.z) {
                // x <= y <= z
                // setsatcomponents(color.x, color.y, color.z, satVal);
                // min, mid, max, val
                
                color.y -= color.x;
                color.z -= color.x;
                color.x = 0.0;
                if (color.z > 0.0) {
                    // max(..., 0.0000001) prevents divide by 0
                    color.y *= satVal/max(color.z, 0.0000001);
                    color.z = satVal;
                }
                
            } else {
                if (color.x <= color.z) {
                    // x <= z <= y
                    // setsatcomponents(color.x, color.z, color.y, satVal);
                    // min, mid, max, value
                    
                    color.z -= color.x;
                    color.y -= color.x;
                    color.x = 0.0;
                    if (color.y > 0.0) {
                        // max(..., 0.0000001) prevents divide by 0
                        color.z *= satVal/max(color.y, 0.0000001);
                        color.y = satVal;
                    }
                } else {
                    // z <= x <= y
                    // setsatcomponents(color.z, color.x, color.y, satVal);
                    // min, mid, max, val
                    
                    color.x -= color.z;
                    color.y -= color.z;
                    color.z = 0.0;
                    if (color.y > 0.0) {
                        // max(..., 0.0000001) prevents divide by 0
                        color.x *= satVal/max(color.y, 0.0000001);
                        color.y = satVal;
                    }
                }
            }
        } else {
            if (color.x <= color.z) {
                // y <= x <= z
                // setsatcomponents(color.y, color.x, color.z, satVal);
                
                color.x -= color.y;
                color.z -= color.y;
                color.y = 0.0;
                if (color.z > 0.0) {
                    // max(..., 0.0000001) prevents divide by 0
                    color.x *= satVal/max(color.z, 0.0000001);
                    color.z = satVal;
                }
            } else {
                if (color.y <= color.z) {
                    // y <= z <= x
                    // setsatcomponents(color.y, color.z, color.x, satVal);
                    // min, mid, max, val
                    
                    color.z -= color.y;
                    color.x -= color.y;
                    color.y = 0.0;
                    if (color.x > 0.0) {
                        // max(..., 0.0000001) prevents divide by 0
                        color.z *= satVal/max(color.x, 0.0000001);
                        color.x = satVal;
                    }
                } else {
                    // z <= y <= x
                    // setsatcomponents(color.z, color.y, color.x, satVal);
                    // min, mid, max, val
                    
                    color.y -= color.z;
                    color.x -= color.z;
                    color.z = 0.0;
                    if (color.x > 0.0) {
                        // max(..., 0.0000001) prevents divide by 0
                        color.y *= satVal/max(color.x, 0.0000001);
                        color.x = satVal;
                    }
                }
            }
        }
        // end setSaturation - result: color ---------------------- //
        
        // intermediate adjustment (setSatResult) -> adjustedcs
        float3 adjVec = (cb - color);
        float adjustment = luminance(adjVec);
        float3 adjustedcs = color + adjustment;
        
        // ClipRGB(adjustedcs) -> blendResult
        
        /* --------------------------------------------------------
         * void clipcolor(inout float3 color)
         *     clips color.
         * input and output float3 color_cl
         * 
         * -------------------------------------------------------- */
        float3 color_cl = adjustedcs;
        float lum_cl = luminance(color_cl);
        float3 lumVec = float3(lum_cl, lum_cl, lum_cl);
        float mini = min3v(color_cl);
        float maxi = max3v(color_cl);
        if (mini < 0.0) {
            mini = lum_cl - mini;
            // max(..., 0.0000001) prevents divide by 0
            color_cl = lumVec + (color_cl - lumVec)*lum_cl/max(mini, 0.0000001);
        }
        if (maxi > 1.0) {
            maxi = maxi - lum_cl;
            // max(..., 0.0000001) prevents divide by 0
            color_cl = lumVec + (color_cl - lumVec)*(1.0 - lum_cl)/max(maxi, 0.0000001);
        }
        // end clipcolor - result: color_cl ---------------------- //
        
        // 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*color_cl.rgb;
    }
}
