/*****************************************************************************
 *
 * ADOBE SYSTEMS INCORPORATED
 * Copyright (C) 2010 Adobe Systems Incorporated
 * All Rights Reserved.
 *
 * NOTICE:  Adobe permits you to use, modify, and distribute this file in 
 * accordance with the terms of the Adobe license agreement accompanying it.  
 * If you have received this file from a source other than Adobe, then your 
 * use, modification, or distribution of it requires the prior written 
 * permission of Adobe.
 *
 *****************************************************************************/

<languageVersion: 1.0;> 
 
// twirl: A simple example to demonstrate the use of the sampling 
//           function to yield an interesting effect.  The filter performs 
//           some math based on the parameter values to change the sampling 
//           location.
kernel twirl
<   namespace : "Pixel Bender Samples";
    vendor : "Adobe Systems";
    version : 2;
    description : "twist an image around"; >
{
    // define PI for the degrees to radians calculation
    const float PI = 3.14159265;
    
    // An input parameter to specify the radius of the twirl effect.
    // For this parameter, we're using metadata to indicate the minimum,
    // maximum, and default values, so that the tools can set the values 
    // in the correctly in the UI for the filter.  
    // NOTE: This parameter indicates how many pixel values out from the 
    // center location we would like to twirl.  The radius is in the pixel
    // coordinate space to ensure that we always rotate in a circle.  For 
    // more information regarding the coordinate spaces, please consult the 
    // pixel bender spec.
    parameter float radius 
    <       
        minValue:float(0.1);
        maxValue:float(2048.0); 
        defaultValue:float(10.0);
    >;
    
    // An input parameter to specify the center of the twirl effect.
    // As above, we're using metadata to indicate the minimum,
    // maximum, and default values, so that the tools can set the values 
    // in the correctly in the UI for the filter.
    parameter float2 center
    <
        minValue:float2(0.0, 0.0);
        maxValue:float2(2048.0, 2048.0);
        defaultValue:float2(256.0, 256.0);
    >;
    
    // An input parameter to specify the angle that we would like to twirl.
    // For this parameter, we're using metadata to indicate the minimum,
    // maximum, and default values, so that the tools can set the values 
    // in the correctly in the UI for the filter.
    parameter float twirlAngle
    <
        minValue:float(0.0);
        maxValue:float(360.0);
        defaultValue:float(90.0);
    >;
    
    // An input parameter that indicates how we want to vary the twirling
    // within the radius.  We've added support to modulate by one of two 
    // functions, a gaussian or a sinc function.  Since Flash does not support
    // bool parameters, we instead are using this as an int with two possible
    // values. Setting this parameter to be 1 will
    // cause the gaussian function to be used, unchecking it will cause 
    // the sinc function to be used.
    parameter int gaussOrSinc
    <
        minValue:int(0);
        maxValue:int(1);
        defaultValue:int(0);
    >;
    
    input image4 oImage;
    output float4 outputColor;

// Region functions are not available in Flash targets, so we only define 
// the functions if we are executing on a different backend.
#if !AIF_FLASH_TARGET

    // needed(): Indicates what area of the input is needed to fulfill the
    //           requested output region.  In this case, we consider a
    //           radius = 1.0 area of the input for each output pixel, so we 
    //           need to outset the area by one.
    region needed(region outputRegion, imageRef inputRef)
    {
        // if we have a gaussian function, negligible rotation occurs at 3 * radius
        // for a sinc function, we want a radius where the value is < 0.03.  That
        // happens at a factor of 33.3 times the radius.
        float2 fallOffPoint = float2( radius, radius );
        if(gaussOrSinc == 0)
            fallOffPoint *= 3.0;
        else
            fallOffPoint *= 33.3;
            
        region rotatedRegion = region(float4(center.x - fallOffPoint.x, 
                                             center.y - fallOffPoint.y,
                                             center.x + fallOffPoint.x, 
                                             center.y + fallOffPoint.y));     
        return union(outputRegion, rotatedRegion);
    }

    // changed(): Indicates what area of the output is affected by the 
    //            specified input.  In this case, we will blur out the image 
    //            by exactly one pixel, so the output is the input outset by 
    //            one.
    region changed(region inputRegion, imageRef inputRef)
    {    
        // if we have a gaussian function, negligible rotation occurs at 3 * radius
        // for a sinc function, we want a radius where the value is < 0.03.  That
        // happens at a factor of 33.3 times the radius.
        float2 fallOffPoint = float2( radius, radius );
        if(gaussOrSinc == 0)
            fallOffPoint *= 3.0;
        else
            fallOffPoint *= 33.3;
            
        region rotatedRegion = region(float4(center.x - fallOffPoint.x, 
                                             center.y - fallOffPoint.y,
                                             center.x + fallOffPoint.x, 
                                             center.y + fallOffPoint.y));   
        return union(inputRegion, rotatedRegion);
    }

#endif

    // evaluatePixel(): The function of the filter that actually does the 
    //                  processing of the image.  This function is called once 
    //                  for each pixel of the output image.
    void
    evaluatePixel()
    {
        // convert the angle to radians
        float twirlAngleRadians = radians(twirlAngle);

        // calculate where we are relative to the center of the twirl
        float2 relativePos = outCoord() - center;
        
        // calculate the absolute distance from the center normalized 
        // by the twirl radius.
        float distFromCenter = length( relativePos );
        distFromCenter /= radius; 
        
        // modulate the angle based on either a gaussian or a sync.
        float adjustedRadians;
        
        // precalculate either the gaussian or the sinc weight
        float sincWeight = sin( distFromCenter ) * twirlAngleRadians / ( distFromCenter );
        float gaussWeight = exp( -1.0 * distFromCenter * distFromCenter ) * twirlAngleRadians;
        
        // protect the algorithm from a 1 / 0 error
        adjustedRadians = (distFromCenter == 0.0) ? twirlAngleRadians : sincWeight;
        
        // switch between a gaussian falloff or a sinc fallof
        adjustedRadians = (gaussOrSinc == 1) ? adjustedRadians : gaussWeight;
        
        // rotate the pixel sample location.
        float cosAngle = cos( adjustedRadians );
        float sinAngle = sin( adjustedRadians );
        
        float2x2 rotationMat = float2x2(
            cosAngle,   sinAngle,
            -sinAngle,  cosAngle
        );
        
        relativePos = rotationMat * relativePos; 
        
        // sample and set as the output color.  since relativePos
        // is related to the center location, we need to add it back in.
        // We use linear sampling to smooth out some of the pixelation.
        outputColor = sampleLinear( oImage, relativePos + center );
    }
}

