/*******************************************************************************
**
** The source file for the Jahshaka effects objects module
** The Jahshaka Project
** Copyright (C) 2000-2005 The Jahshaka Project
** Released under the GNU General Public License
**
*******************************************************************************/

#include "glworld.h"
#include "glcore.h"
#include "coreobject.h"
#include <jahpreferences.h>

/////////////////////////////////////////////////////////////
//the gpu color corrector now selects the correct routine
//based on gpu capability
//we should add a GLSL version here as well

void 
GLWorld::doColorizeGPU(JahLayer* base_layer) 
{

    static bool testarbsupport = false;
    static bool arbwillwork = false;

    if ( jahstd::glslSupport() && nv_gpusupport ) //nvidia support for the color corrector
    {
        //doNV30ColorizeGPU(base_layer);
        doColorCorrectorGLSL(base_layer);
    }
    else if ( gpusupport ) //nvidia support for the color corrector
    {

        //we need to check if the ARB is supported on this GPU
        if (!testarbsupport)
        {
            JahPrefs& jprefs    = JahPrefs::getInstance();

            QString sourceName = JahBasePath+"source/Jahshaka/JahLibraries/shaders/color_frag_arb_gpu.fp";

            int themax_vp_instructions =  jprefs.getMaxVertexInstructionsARB();
            int themax_fp_instructions =  jprefs.getMaxFragmentInstructionsARB();

            arbwillwork  =  checkComplexArbSupport(sourceName, themax_fp_instructions, themax_vp_instructions );
            testarbsupport = true;
        }

        if (arbwillwork)
            doArbColorizeGPU(base_layer);
    }
}

////////////////////////////////////////////////////////
// the NVidia based colorize module

void 
GLWorld::doNV30ColorizeGPU(JahLayer* base_layer) 
{
    float                                       brightness;
    float4                                      color;
    float4                                      enhance;
    float4                                      hsv;

    GLint                                       width;
    GLint                                       height;

    GLuint                                      fragment_program_handle;

    unsigned char*                              fragment_program = NULL;

    static int                                  count = 0;

    ////////////////////////////////////////////
    //initialize variables and settings

    width = (int)base_layer->textureData.width;
    height = (int)base_layer->textureData.height;

    float float_width = (float)width;
    float float_height = (float)height;

    motionNode* motion_node = getCurrentEffectLayer()->layernodes->m_node;

    //getCurrentEffectLayer()->layernodes->m_node->rx
    color.x   = motion_node->rx / 1000.0f;
    color.y = motion_node->ry / 1000.0f;
    color.z  = motion_node->rz / 1000.0f;
    color.w  = motion_node->tx / 1000.0f;
    brightness = motion_node->ty / 1000.0f;
    enhance.y = (motion_node->tz / -1000.0f) + 1.0f;
    hsv.x = (motion_node->sx - 1) / 10;
    hsv.y = (motion_node->sy - 1) / 10;
    hsv.z = (motion_node->sz - 1) * 100;

    enhance.x = 0.0f;


    glActiveTextureARB(GL_TEXTURE0_ARB);
    base_layer->bindBaseOrCompositeTexture();


    ////////////////////////////////////////////
    //load the fragment shader

    if (count == 0)
    {
        fragment_program = loadshaderfile(JahBasePath+"source/Jahshaka/JahLibraries/shaders/color_frag_gpu.fp");
    }

    glGenProgramsNV(1, &fragment_program_handle);
    glLoadProgramNV(GL_FRAGMENT_PROGRAM_NV, fragment_program_handle,
                    (GLsizei)strlen((char*)fragment_program), (const GLubyte*)fragment_program);

    if (glGetError() == GL_INVALID_OPERATION)
    {
        find_shader_program_error(fragment_program, "color_frag_gpu.fp");
    }

    ////////////////////////////////////////////
    //load the fragment shader with settings


    glProgramNamedParameter4fNV(fragment_program_handle, (GLsizei)strlen("color_scale"),
        (const GLubyte*)"color_scale", color.x, color.y, color.z, color.w);

    glProgramNamedParameter4fNV(fragment_program_handle, (GLsizei)strlen("brightness"),
        (const GLubyte*)"brightness", brightness, 0.0f, 0.0f, 0.0f);

    glProgramNamedParameter4fNV(fragment_program_handle, (GLsizei)strlen("enhance"),
        (const GLubyte*)"enhance", enhance.x, enhance.y, 0.0f, 0.0f);

    glProgramNamedParameter4fNV(fragment_program_handle, (GLsizei)strlen("hsv"),
        (const GLubyte*)"hsv", hsv.x, hsv.y, hsv.z, 0.0f);

    glEnable(GL_FRAGMENT_PROGRAM_NV);
    glBindProgramNV(GL_FRAGMENT_PROGRAM_NV, fragment_program_handle);

    ////////////////////////////////////////////
    //run the effects on the gpu now

    float texwidthratio = base_layer->textureData.texwidthratio;
    float texheightratio = base_layer->textureData.texheightratio;

    glBegin(GL_QUADS);

            glMultiTexCoord4fARB(GL_TEXTURE0_ARB, 0.0, 0.0, 0.0, 1.0);
            glVertex2f( -float_width / 2.0, -float_height / 2.0);

            glMultiTexCoord4fARB(GL_TEXTURE0_ARB,
                                texwidthratio, 0.0, 0.0, 1.0);
            glVertex2f( float_width / 2.0, -float_height / 2.0);

            glMultiTexCoord4fARB(GL_TEXTURE0_ARB,
                                texwidthratio,
                                texheightratio, 0.0, 1.0);
            glVertex2f( float_width / 2.0, float_height / 2.0);

            glMultiTexCoord4fARB(GL_TEXTURE0_ARB, 0.0, texheightratio, 0.0, 1.0);
            glVertex2f( -float_width / 2.0, float_height / 2.0);


    glEnd(); //GL_QUADS

    ////////////////////////////////////////////
    //grab a copy of the resulting image into the buffer

    glBindTexture(GL_TEXTURE_2D, base_layer->getTextureComposite().texID );
    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (core->m_renderwidth - width) / 2,
                        (core->m_renderheight - height) / 2, width, height);

    ////////////////////////////////////////////
    //clean up on the way out

    glDisable(GL_FRAGMENT_PROGRAM_NV);

    glDeleteProgramsNV(1, &fragment_program_handle);
    
}

////////////////////////////////////////////////////////
// the ARB based colorize module

void 
GLWorld::doArbColorizeGPU(JahLayer* base_layer) 
{
    float                                       brightness;
    float4                                      color;
    float4                                      enhance;
    float4                                      hsv;

    GLint                                       width;
    GLint                                       height;

    GLuint                                      fragment_program_handle;

    unsigned char*                              fragment_program = NULL;

    static int                                  count = 0;

    ////////////////////////////////////////////
    //initialize variables and settings

    width = (int)base_layer->textureData.width;
    height = (int)base_layer->textureData.height;

    float float_width = (float)width;
    float float_height = (float)height;

    motionNode* motion_node = getCurrentEffectLayer()->layernodes->m_node;

    //getCurrentEffectLayer()->layernodes->m_node->rx
    color.x   = motion_node->rx / 1000.0f;
    color.y = motion_node->ry / 1000.0f;
    color.z  = motion_node->rz / 1000.0f;
    color.w  = motion_node->tx / 1000.0f;
    brightness = motion_node->ty / 1000.0f;
    enhance.y = (motion_node->tz / -1000.0f) + 1.0f;
    hsv.x = (motion_node->sx - 1) / 10;
    hsv.y = (motion_node->sy - 1) / 10;
    hsv.z = (motion_node->sz - 1) * 100;

    enhance.x = 0.0f;

    glActiveTextureARB(GL_TEXTURE0_ARB);
    base_layer->bindBaseOrCompositeTexture();

    ////////////////////////////////////////////
    //load the fragment shader

    if (count == 0)
    {
        fragment_program = loadshaderfile(JahBasePath+"source/Jahshaka/JahLibraries/shaders/color_frag_arb_gpu.fp");
    }

    glGenProgramsARB(1, &fragment_program_handle);

    glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fragment_program_handle);
    glProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
                        (GLsizei)strlen((char*)fragment_program), (const GLubyte*)fragment_program);

    if (glGetError() == GL_INVALID_OPERATION)
    {
        find_shader_program_error(fragment_program, "color_frag_arb_gpu.fp");
    }

    ////////////////////////////////////////////
    //load the fragment shader with settings


    glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, color.x, color.y, color.z, 0.0f);
    glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, brightness, 0.0f, 0.0f, 0.0f);
    glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 2, enhance.x, enhance.y, 0.0f, 0.0f);
    glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 3, hsv.x, hsv.y, hsv.z, 0.0f);

    glEnable(GL_FRAGMENT_PROGRAM_ARB);

    //glBindProgramNV(GL_FRAGMENT_PROGRAM_NV, fragment_program_handle);

    ////////////////////////////////////////////
    //run the effects on the gpu now

    float texwidthratio = base_layer->textureData.texwidthratio;
    float texheightratio = base_layer->textureData.texheightratio;

    glBegin(GL_QUADS);

            glMultiTexCoord4fARB(GL_TEXTURE0_ARB, 0.0, 0.0, 0.0, 1.0); 
            glVertex2f( -float_width / 2.0, -float_height / 2.0);

            glMultiTexCoord4fARB(GL_TEXTURE0_ARB,
                                texwidthratio, 0.0, 0.0, 1.0);
            glVertex2f( float_width / 2.0, -float_height / 2.0);

            glMultiTexCoord4fARB(GL_TEXTURE0_ARB, 
                                texwidthratio,
                                texheightratio, 0.0, 1.0);
            glVertex2f( float_width / 2.0, float_height / 2.0);

            glMultiTexCoord4fARB(GL_TEXTURE0_ARB, 0.0, texheightratio, 0.0, 1.0); 
            glVertex2f( -float_width / 2.0, float_height / 2.0);


    glEnd(); //GL_QUADS

    ////////////////////////////////////////////
    //grab a copy of the resulting image into the buffer

    glBindTexture(GL_TEXTURE_2D, base_layer->getTextureComposite().texID );
    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (core->m_renderwidth - width) / 2,
                        (core->m_renderheight - height) / 2, width, height);

    ////////////////////////////////////////////
    //clean up on the way out

    glDisable(GL_FRAGMENT_PROGRAM_ARB);

    glDeleteProgramsARB(1, &fragment_program_handle);
    
}

#if 0
void
rgb_to_hsv(float4& hsv, float4& rgb)
{
    // rgb range 0 -255
    float maximum = rgb.max3();
    float minimum = rgb.min3();

    hsv.z = maximum;
    float delta = maximum - minimum;

    if (delta > 0.0001)
    {
        hsv.y = delta / maximum;

        if (rgb.x == maximum)
        {
            hsv.x = (rgb.y - rgb.z) / delta;

            if (hsv.x < 0)
            {
                hsv.x += 6;
            }
        }
        else if (rgb.y == maximum)
        {
            hsv.x = 2 + (rgb.z - rgb.x) / delta;
        }
        else if (rgb.z == maximum)
        {
            hsv.x = 4 + (rgb.x - rgb.y) / delta;
        }

        hsv.x /= 6;
    }
    else
    {
        hsv.x = 0;
        hsv.y = 0;
    }

    hsv.w = rgb.w;
}

void 
hsv_to_rgb(float4& rgb, float4& hsv)
{
    // rgb range 0 -255
    if (hsv.y == 0)
    {
        rgb = hsv.z;
    }
    else
    {
        float hue = hsv.x;

        if (hue == 1)
        {
            hue = 0;
        }

        hue *= 6;

        int i = ( int )floorf(hue);
        float f = hue - i;
        float w = hsv.z * (1 - hsv.y);
        float q = hsv.z * ( 1 - (hsv.y * f) );
        float t = hsv.z * ( 1 - (hsv.y * (1 - f) ) );

        if (i == 0)
        {
            rgb.x = hsv.z;
            rgb.y = t;
            rgb.z = w;
        }
        else if (i == 1)
        {
            rgb.x = q;
            rgb.y = hsv.z;
            rgb.z = w;
        }
        else if (i == 2)
        {
            rgb.x = w;
            rgb.y = hsv.z;
            rgb.z = t;
        }
        else if (i == 3)
        {
            rgb.x = w;
            rgb.y = q;
            rgb.z = hsv.z;
        }
        else if (i == 4)
        {
            rgb.x = t;
            rgb.y = w;
            rgb.z = hsv.z;
        }
        else if (i == 5)
        {
            rgb.x = hsv.z;
            rgb.y = w;
            rgb.z = q;
        }
    }

    rgb.w = hsv.w;
}


void   
color_enhance2( float4& rgb, float4& hsv_in, float4& enhance )
{
      
    float4 hsv;
    rgb_to_hsv(hsv, rgb );

    hsv.x += hsv_in.x;
    hsv.y *= hsv_in.y; // multiplicative saturation. Range is -1 to +3, with 1.0 center.
    hsv.z += hsv_in.z;

    // important! wrap the h value, dont rely on 'clamp' later, so we do a *real* hue shift.
    if ( hsv.x < 0.0f ) 
    {
        hsv.x += 1.0f;
    }
    else if ( hsv.x > 1.0f ) 
    {
        hsv.x-= 1.0f;
    }
    // similarly, clamp the saturation before doing RGB convert
    if ( hsv.y < 0.0f ) 
    {
        hsv.y = 0.0f;
    }
    else if ( hsv.y > 1.0f ) 
    {
        hsv.y = 1.0f;
    }

    // We store the brightness so that after tint we can restore it.
    float storeval = hsv.z;

    // Now apply the tint. The old enhance.y component is unused. 
    float tint = enhance.x; // 0 - 3600
    float str = enhance.z / 100.0f; // 0 - 1
    
    if ( str > 0.0001f )
    {  
        float4 tinthsv;
        float4 tintrgb;
        tinthsv.x = 0.5 -  ((tint + 900.0f) / ( 3600.0f)); // convert from 1/10th degrees + shift 90 degrees and reverse hue.

        if ( tinthsv.x  < 0.0f ) 
        {
            tinthsv.x += 1.0f;
        }
        else if ( tinthsv.x > 1.0f ) 
        {
            tinthsv.x -= 1.0f;
        }

        tinthsv.y = 1.0;
        tinthsv.z = 1.0;
        hsv_to_rgb( tintrgb, tinthsv );

        if ( 0 ) printf ( "# rgb start %f %f %f \n", rgb.x, rgb.y, rgb.z );   
        if ( 0 ) printf ( "# tintrgb %f %f %f \n", tintrgb.x, tintrgb.y, tintrgb.z );

        hsv_to_rgb(rgb, hsv );
        
        // Add the rgb tint. The '200' is arbitrary, but colors top out before 255, so scale back the strength
        // a little.
        float delr = 200.0f * str* tintrgb.x;
        float delg = 200.0f * str* tintrgb.y;
        float delb = 200.0f * str* tintrgb.z;
        
        rgb.x += delr;
        rgb.y += delg;
        rgb.z += delb;
        rgb.clamp();

        // must now restore orig brightness. I think this part of the algorithm is not strictly correct, 
        // should we preserve contrast, luminosity? Compare with Combustn, its v close but not identical.
        // Also, not all tint changes will change the brightness by the same amount. Same applies to saturation.
        rgb_to_hsv( hsv, rgb  );
        if ( 0 ) printf ( "# hsv before resat %f %f %f \n", hsv.x, hsv.y, hsv.z );
        hsv.z = storeval; // (see note above)
        
        // Final value back to rgb.
        hsv_to_rgb(rgb, hsv );
        
        // (calling function can do rgb.clamp again, we dont here)

        if ( 0 ) printf ( "finalrgb %f %f %f \n", rgb.x, rgb.y, rgb.z );


        /* OLD TINT CODE. Tried literally adding HS vector to each color. Aside from being slow, I dont think
                            this is how its done anyway (despite seeming clever). See above (simpler!) method.
        // Pity need to keep doing this conversion every time (its the same)...
        float4 tinthsv;
        float4 tintrgb;
        tinthsv.x = 0.5 -  ((tint + 900.0) / ( 3600.0)); // our hue circle has green at 0 degrees, and units of 1/10th deg.

        if ( 0 ) printf("tinthsv.x = %f \n", tinthsv.x );
        if ( tinthsv.x  < 0 ) tinthsv.x += 1.0;
        else if ( tinthsv.x > 1.0 ) tinthsv.x -= 1.0;
        tinthsv.y = 1.0;
        tinthsv.z = 1.0;
     
        if ( 0 ) printf ( "# hsv %f %f %f \n", hsv.x, hsv.y, hsv.z );
        if ( 0 ) printf ( "# tinthsv %f %f %f \n", tinthsv.x, tinthsv.y, tinthsv.z );
        
        float angle = PI/2 + hsv.x * TWO_PI; // ie maps to 0-2PI. NB 90 deg shift due to circle.
        if ( angle > TWO_PI ) angle -= TWO_PI;
       
        float tintangle =  PI - enhance.x/3600.0 * TWO_PI; // ie maps to 0-2PI.
     
        float newx = hsv.y * cos(angle) + ( str * cos ( tintangle ) );
        float newy = hsv.y * sin(angle) + ( str * sin ( tintangle ) );
       
        float newsat;
        newsat = sqrt( newx*newx + newy*newy );

        float newangle = acos ( newx / newsat );
        if ( newy < 0.0 ) newangle = TWO_PI - newangle; // acos returns 0-2PI range.
        if ( newsat > 1.0 ) newsat = 1.0;
        
        if ( 0 ) printf ("newangle - pi/2 %f \n", newangle - PI/2.0 );

        float newhue = ( (newangle - PI/2.0 ) / TWO_PI ); // map back the 90 degree shift, and 0-1.0.

        if ( newhue > 1.0 ) newhue -= 1.0;
        else if ( newhue < 0.0 ) newhue += 1.0;

        if ( 0 ) printf("# angle = %f \n", angle );
        if ( 0 ) printf("# newx newy = %f %f \n", newx, newy );
        if ( 0 ) printf("# newangle = %f \n", newangle );
        if ( 0 ) printf("# newsat = %f \n", newsat );
        if ( 0 ) printf("# newhue = %f \n", newhue );

        hsv.x = newhue;
       // (maybe? ) hsv.y = newsat;

        // clamp hue and sat
        if ( hsv.y < 0.0 ) hsv.y = 0.0;
        if ( hsv.y > 1.0 ) hsv.y = 1.0;
        if ( hsv.x < 0.0 ) hsv.x = 0.0;
        if ( hsv.x > 1.0 ) hsv.x = 1.0;

        hsv_to_rgb(rgb, hsv );

        if ( 0 ) printf ( "finalrgb %f %f %f \n", rgb.x, rgb.y, rgb.z );
        */ // (OLD TINT CODE END HERE) 

    }
    else
    {   // just clamp
        if ( hsv.x < 0.0f ) hsv.x = 0.0f;
        if ( hsv.x > 1.0f ) hsv.x = 1.0f;
        hsv_to_rgb(rgb, hsv );
        
    }
}
#endif

const char*
GLWorld::m_color_correction_fp40_cgc_15_bug_workaround_fragment_shader = 
{
    "    !!ARBfp1.0                                                 \n"
    "    OPTION NV_fragment_program2;                               \n"
    "    # cgc version 1.4.0000, build date Sep 26 2005 21:50:31    \n"
    "    # command line args: -oglsl -profile fp40                  \n"
    "    # source file: cgctest.glsl                                \n"
    "    #vendor NVIDIA Corporation                                 \n"
    "    #version 1.0.02                                            \n"
    "    #profile fp40                                              \n"
    "    #program main                                              \n"
    "    #semantic base_texture                                     \n"
    "    #semantic brightness                                       \n"
    "    #semantic gamma                                            \n"
    "    #semantic hsv                                              \n"
    "    #semantic color_scale                                      \n"
    "    #semantic enhance                                          \n"
    "    #var float4 gl_TexCoord[0] : $vin.TEX0 : TEX0 : -1 : 1     \n"
    "    #var float4 gl_TexCoord[1] :  :  : -1 : 0                  \n"
    "    #var float4 gl_TexCoord[2] :  :  : -1 : 0                  \n"
    "    #var float4 gl_TexCoord[3] :  :  : -1 : 0                  \n"
    "    #var float4 gl_TexCoord[4] :  :  : -1 : 0                  \n"
    "    #var float4 gl_TexCoord[5] :  :  : -1 : 0                  \n"
    "    #var float4 gl_TexCoord[6] :  :  : -1 : 0                  \n"
    "    #var float4 gl_TexCoord[7] :  :  : -1 : 0                  \n"
    "    #var float4 gl_FragColor : $vout.COLOR : COL : -1 : 1      \n"
    "    #var sampler2D base_texture :  : texunit 0 : -1 : 1        \n"
    "    #var float brightness :  : c[1] : -1 : 1                   \n"
    "    #var float gamma :  : c[8] : -1 : 1                        \n"
    "    #var float3 hsv :  : c[3] : -1 : 1                         \n"
    "    #var float4 color_scale :  : c[2] : -1 : 1                 \n"
    "    #var float3 enhance :  : c[6] : -1 : 1                     \n"
    "    #const c[0] = 255 0.0001 0 1                               \n"
    "    #const c[4] = 4 2 6 0.1666667                              \n"
    "    #const c[5] = 0.01 900 0.0002777778 0.5                    \n"
    "    #const c[7] = 3 5 200 0.003921569                          \n"
    "    PARAM c[9] = { { 255, 9.9999997e-005, 0, 1 },              \n"
    "               program.local[1..3],                            \n"
    "               { 4, 2, 6, 0.16666667 },                        \n"
    "               { 0.0099999998, 900, 0.00027777778, 0.5 },      \n"
    "               program.local[6],                               \n"
    "               { 3, 5, 200, 0.0039215689 },                    \n"
    "               program.local[8] };                             \n"
    "    TEMP R0;                                                   \n"
    "    TEMP R1;                                                   \n"
    "    TEMP R2;                                                   \n"
    "    TEMP R3;                                                   \n"
    "    TEMP R4;                                                   \n"
    "    SHORT TEMP H0;                                             \n"
    "    SHORT TEMP H1;                                             \n"
    "    TEMP RC;                                                   \n"
    "    TEMP HC;                                                   \n"
    "    OUTPUT oCol = result.color;                                \n"
    "    TEX   R0.xyz, fragment.texcoord[0], texture[0], 2D;        \n"
    "    MULR  R0.xyz, R0, c[0].x;                                  \n"
    "    ADDR  R0.xyz, R0, c[1].x;                                  \n"
    "    ADDR  R0.x, R0, c[2];                                      \n"
    "    ADDR  R0.y, R0, c[2];                                      \n"
    "    ADDR  R3.w, R0.z, c[2].z;                                  \n"
    "    MOVR  R0.w, R0.x;                                          \n"
    "    SLTRC HC.x, R0.y, R0;                                      \n"
    "    MOVR  R0.w(GT.x), R0.y;                                    \n"
    "    MOVR  R0.z, R0.x;                                          \n"
    "    MOVR  R2.w, R1.x;                                          \n"
    "    MOVR  R1.w, R0;                                            \n"
    "    SLTRC HC.x, R3.w, R0.w;                                    \n"
    "    MOVR  R1.w(GT.x), R3;                                      \n"
    "    SGTRC HC.x, R0.y, R0;                                      \n"
    "    MOVR  R0.z(GT.x), R0.y;                                    \n"
    "    MOVR  R0.w, R0.z;                                          \n"
    "    SGTRC HC.x, R3.w, R0.z;                                    \n"
    "    MOVR  R0.w(GT.x), R3;                                      \n"
    "    ADDR  R0.z, R0.w, -R1.w;                                   \n"
    "    SGTR  H0.x, R0.z, c[0].y;                                  \n"
    "    SEQR  H0.y, R0.x, R0.w;                                    \n"
    "    MULX  H0.z, H0.x, H0.y;                                    \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    RCPR  R4.y, R0.z;                                          \n"
    "    ADDR  R1.w, R0.y, -R3;                                     \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MULR  R2.w(GT.x), R1, R4.y;                                \n"
    "    SLTR  H0.w, R2, c[0].z;                                    \n"
    "    MULXC HC.x, H0.z, H0.w;                                    \n"
    "    MOVR  R1.w, R2;                                            \n"
    "    ADDR  R1.w(GT.x), R2, c[4].z;                              \n"
    "    ADDR  R2.w, R3, -R0.x;                                     \n"
    "    SEQR  H0.z, R0.y, R0.w;                                    \n"
    "    MULX  H0.y, H0.x, H0;                                      \n"
    "    MULXC HC.x, H0.y, H0.z;                                    \n"
    "    SEQX  H0.z, H0, c[0];                                      \n"
    "    MULX  H0.y, H0, H0.z;                                      \n"
    "    MADR  R1.w(GT.x), R2, R4.y, c[4].y;                        \n"
    "    SEQR  H0.z, R3.w, R0.w;                                    \n"
    "    ADDR  R0.x, R0, -R0.y;                                     \n"
    "    MULXC HC.x, H0.y, H0.z;                                    \n"
    "    MADR  R1.w(GT.x), R0.x, R4.y, c[4].x;                      \n"
    "    MOVR  R0.x, c[0].z;                                        \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MULR  R0.x(NE), R1.w, c[4].w;                              \n"
    "    ADDR  R1.w, R0.x, c[3].x;                                  \n"
    "    SLTR  H0.y, R0.x, -c[3].x;                                 \n"
    "    MOVRC HC.x, H0.y;                                          \n"
    "    MOVR  R0.y, R1.w;                                          \n"
    "    ADDR  R0.y(GT.x), R1.w, c[0].w;                            \n"
    "    MOVR  R0.x, R0.y;                                          \n"
    "    SGTR  H0.z, R0.y, c[0].w;                                  \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULXC HC.x, H0.y, H0.z;                                    \n"
    "    ADDR  R0.x(GT), R0.y, -c[0].w;                             \n"
    "    RCPR  R0.y, R0.w;                                          \n"
    "    MULR  R0.y, R0.z, R0;                                      \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R0.y(EQ.x), c[0].z;                                  \n"
    "    MULR  R1.w, R0.y, c[3].y;                                  \n"
    "    SLTR  H0.x, R1.w, c[0].z;                                  \n"
    "    MOVR  R0.z, R1.w;                                          \n"
    "    MOVRC HC.x, R1.w;                                          \n"
    "    MOVR  R0.z(LT.x), c[0];                                    \n"
    "    MOVR  R1.w, c[6].z;                                        \n"
    "    MULR  R2.w, c[5].x, R1;                                    \n"
    "    MOVR  R0.y, R0.z;                                          \n"
    "    SGTR  H0.y, R0.z, c[0].w;                                  \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULXC HC.x, H0, H0.y;                                      \n"
    "    MOVR  R0.y(GT.x), c[0].w;                                  \n"
    "    ADDR  R0.z, R0.w, c[3];                                    \n"
    "    SGTRC HC.x, R2.w, c[0].y;                                  \n"
    "    ADDR  R0.w, R0, c[3].z;                                    \n"
    "    MOVR  R1.w, R2;                                            \n"
    "    IF    GT.x;                                                \n"
    "    MOVR  R2.w, c[5].y;                                        \n"
    "    ADDR  R2.w, c[6].x, R2;                                    \n"
    "    MULR  R2.w, R2, c[5].z;                                    \n"
    "    SLTR  H0.x, -R2.w, -c[5].w;                                \n"
    "    ADDR  R3.w, -R2, c[5];                                     \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R2.w, R3;                                            \n"
    "    ADDR  R2.w(GT.x), R3, c[0];                                \n"
    "    MOVR  R3.xyz, R3;                                          \n"
    "    MOVR  R3.w, R2;                                            \n"
    "    SGTR  H0.y, R2.w, c[0].w;                                  \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULXC HC.x, H0, H0.y;                                      \n"
    "    ADDR  R3.w(GT.x), R2, -c[0];                               \n"
    "    MOVR  R2.w, R3;                                            \n"
    "    SEQRC HC.x, R3.w, c[0].w;                                  \n"
    "    MOVR  R2.w(GT.x), c[0].z;                                  \n"
    "    MULR  R2.w, R2, c[4].z;                                    \n"
    "    FLRR  R3.w, R2;                                            \n"
    "    ADDR  R4.y, R2.w, -R3.w;                                   \n"
    "    ADDR  R2.w, -R4.y, c[0];                                   \n"
    "    MOVRC HC.x, R3.w;                                          \n"
    "    SEQR  H0.x, R3.w, c[0].z;                                  \n"
    "    MOVR  R3.x(EQ), c[0].w;                                    \n"
    "    ADDR  R4.y, R4, c[0].z;                                    \n"
    "    SEQR  H0.y, R3.w, c[0].w;                                  \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULX  H1.y, H0.x, H0;                                      \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R3.w, c[4];                                    \n"
    "    MULX  H1.x, H0, H0.y;                                      \n"
    "    MOVR  R3.x(GT), R2.w;                                      \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R3.w, c[7].x;                                  \n"
    "    MULX  H0.w, H0.x, H0.y;                                    \n"
    "    MOVR  R3.x(GT), c[0].z;                                    \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R3.w, c[4].x;                                  \n"
    "    MULX  H0.z, H0.x, H0.y;                                    \n"
    "    MOVR  R3.x(GT), c[0].z;                                    \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R3.w, c[7];                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    MOVR  R3.x(GT), R4.y;                                      \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R3.x(GT), c[0].w;                                    \n"
    "    MOVRC HC.x, R3.w;                                          \n"
    "    MOVR  R3.z(EQ.x), c[0];                                    \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    MOVR  R3.z(GT.x), c[0];                                    \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    MOVR  R3.z(GT.x), R4.y;                                    \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    MOVR  R3.z(GT.x), c[0].w;                                  \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MOVR  R3.z(GT.x), c[0].w;                                  \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R3.z(GT.x), R2.w;                                    \n"
    "    MOVRC HC.x, R3.w;                                          \n"
    "    MOVR  R3.y(EQ.x), R4;                                      \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    MOVR  R3.y(GT.x), c[0].w;                                  \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    MOVR  R3.y(GT.x), c[0].w;                                  \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    MOVR  R3.y(GT.x), R2.w;                                    \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MOVR  R3.y(GT.x), c[0].z;                                  \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R3.y(GT.x), c[0].z;                                  \n"
    "    MOVRC HC.x, R0.y;                                          \n"
    "    IF    EQ.x;                                                \n"
    "    MOVR  R2.xyz, R0.z;                                        \n"
    "    ELSE;                                                      \n"
    "    MOVR  R2.xyz, R2;                                          \n"
    "    MADR  R3.w, -R0.y, R0.z, R0.z;                             \n"
    "    MOVR  R2.w, R0.x;                                          \n"
    "    SEQRC HC.x, R0, c[0].w;                                    \n"
    "    MOVR  R2.w(GT.x), c[0].z;                                  \n"
    "    MULR  R0.x, R2.w, c[4].z;                                  \n"
    "    FLRR  R2.w, R0.x;                                          \n"
    "    ADDR  R4.y, R0.x, -R2.w;                                   \n"
    "    MULR  R0.x, R0.y, R4.y;                                    \n"
    "    MOVRC HC.x, R2.w;                                          \n"
    "    SEQR  H0.x, R2.w, c[0].z;                                  \n"
    "    MADR  R0.y, -R4, R0, R0;                                   \n"
    "    MOVR  R2.x(EQ), R0.z;                                      \n"
    "    MADR  R0.x, -R0, R0.z, R0.z;                               \n"
    "    MADR  R0.y, -R0, R0.z, R0.z;                               \n"
    "    SEQR  H0.y, R2.w, c[0].w;                                  \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULX  H1.y, H0.x, H0;                                      \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R2.w, c[4];                                    \n"
    "    MULX  H1.x, H0, H0.y;                                      \n"
    "    MOVR  R2.x(GT), R0;                                        \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R2.w, c[7].x;                                  \n"
    "    MULX  H0.w, H0.x, H0.y;                                    \n"
    "    MOVR  R2.x(GT), R3.w;                                      \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R2.w, c[4].x;                                  \n"
    "    MULX  H0.z, H0.x, H0.y;                                    \n"
    "    MOVR  R2.x(GT), R3.w;                                      \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R2.w, c[7];                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    MOVR  R2.x(GT), R0.y;                                      \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R2.x(GT), R0.z;                                      \n"
    "    MOVRC HC.x, R2.w;                                          \n"
    "    MOVR  R2.z(EQ.x), R3.w;                                    \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    MOVR  R2.z(GT.x), R3.w;                                    \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    MOVR  R2.z(GT.x), R0.y;                                    \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    MOVR  R2.z(GT.x), R0;                                      \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MOVR  R2.z(GT.x), R0;                                      \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R2.z(GT.x), R0.x;                                    \n"
    "    MOVRC HC.x, R2.w;                                          \n"
    "    MOVR  R2.y(EQ.x), R0;                                      \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    MOVR  R2.y(GT.x), R0.z;                                    \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    MOVR  R2.y(GT.x), R0.z;                                    \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    MOVR  R2.y(GT.x), R0.x;                                    \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MOVR  R2.y(GT.x), R3.w;                                    \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R2.y(GT.x), R3.w;                                    \n"
    "    ENDIF;                                                     \n"
    "    MOVR  R2.w, R4.x;                                          \n"
    "    MULR  R0.x, R1.w, R3;                                      \n"
    "    MULR  R0.x, R0, c[7].z;                                    \n"
    "    ADDR  R0.y, R2.x, R0.x;                                    \n"
    "    SLTR  H0.x, R2, -R0;                                       \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R0.x, R0.y;                                          \n"
    "    SGTR  H0.y, R0, c[0].x;                                    \n"
    "    MULR  R0.y, R1.w, R3;                                      \n"
    "    MULR  R0.y, R0, c[7].z;                                    \n"
    "    MULR  R1.w, R1, R3.z;                                      \n"
    "    MULR  R2.x, R1.w, c[7].z;                                  \n"
    "    MOVR  R0.x(GT), c[0].z;                                    \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULXC HC.x, H0, H0.y;                                      \n"
    "    ADDR  R0.z, R2.y, R0.y;                                    \n"
    "    SLTR  H0.x, R2.y, -R0.y;                                   \n"
    "    MOVR  R0.x(GT), c[0];                                      \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R0.y, R0.z;                                          \n"
    "    SGTR  H0.y, R0.z, c[0].x;                                  \n"
    "    MOVR  R0.y(GT.x), c[0].z;                                  \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULXC HC.x, H0, H0.y;                                      \n"
    "    MOVR  R0.y(GT.x), c[0].x;                                  \n"
    "    ADDR  R1.w, R2.z, R2.x;                                    \n"
    "    SLTR  H0.x, R2.z, -R2;                                     \n"
    "    MOVR  R2.x, R1.w;                                          \n"
    "    MOVR  R0.z, R0.x;                                          \n"
    "    SLTRC HC.x, R0.y, R0;                                      \n"
    "    MOVR  R0.z(GT.x), R0.y;                                    \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R2.x(GT), c[0].z;                                    \n"
    "    SGTR  H0.y, R1.w, c[0].x;                                  \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULXC HC.x, H0, H0.y;                                      \n"
    "    MOVR  R2.x(GT), c[0];                                      \n"
    "    MOVR  R2.y, R0.z;                                          \n"
    "    SLTRC HC.x, R2, R0.z;                                      \n"
    "    MOVR  R2.y(GT.x), R2.x;                                    \n"
    "    MOVR  R0.z, R0.x;                                          \n"
    "    SGTRC HC.x, R0.y, R0;                                      \n"
    "    MOVR  R0.z(GT.x), R0.y;                                    \n"
    "    MOVR  R1.w, R0.z;                                          \n"
    "    SGTRC HC.x, R2, R0.z;                                      \n"
    "    MOVR  R1.w(GT.x), R2.x;                                    \n"
    "    ADDR  R2.y, R1.w, -R2;                                     \n"
    "    SGTR  H0.x, R2.y, c[0].y;                                  \n"
    "    SEQR  H0.y, R0.x, R1.w;                                    \n"
    "    MULX  H0.z, H0.x, H0.y;                                    \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    RCPR  R2.z, R2.y;                                          \n"
    "    ADDR  R0.z, R0.y, -R2.x;                                   \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MULR  R2.w(GT.x), R0.z, R2.z;                              \n"
    "    SLTR  H0.w, R2, c[0].z;                                    \n"
    "    MULXC HC.x, H0.z, H0.w;                                    \n"
    "    MOVR  R0.z, R2.w;                                          \n"
    "    ADDR  R0.z(GT.x), R2.w, c[4];                              \n"
    "    ADDR  R2.w, R2.x, -R0.x;                                   \n"
    "    ADDR  R0.x, R0, -R0.y;                                     \n"
    "    SEQR  H0.z, R0.y, R1.w;                                    \n"
    "    MULX  H0.y, H0.x, H0;                                      \n"
    "    MULXC HC.x, H0.y, H0.z;                                    \n"
    "    SEQX  H0.z, H0, c[0];                                      \n"
    "    MULX  H0.y, H0, H0.z;                                      \n"
    "    MADR  R0.z(GT.x), R2.w, R2, c[4].y;                        \n"
    "    SEQR  H0.z, R2.x, R1.w;                                    \n"
    "    MULXC HC.x, H0.y, H0.z;                                    \n"
    "    MADR  R0.z(GT.x), R0.x, R2, c[4].x;                        \n"
    "    MOVR  R0.x, c[0].z;                                        \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R4.x, c[0].z;                                        \n"
    "    MULR  R4.x(NE), R0.z, c[4].w;                              \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    RCPR  R0.y, R1.w;                                          \n"
    "    MULR  R0.x(NE), R2.y, R0.y;                                \n"
    "    MOVRC HC.x, R0;                                            \n"
    "    MOVR  R4.y, R0.x;                                          \n"
    "    IF    EQ.x;                                                \n"
    "    MOVR  R1.xyz, R0.w;                                        \n"
    "    ELSE;                                                      \n"
    "    MOVR  R0.x, R4;                                            \n"
    "    SEQRC HC.x, R4, c[0].w;                                    \n"
    "    MOVR  R0.x(GT), c[0].z;                                    \n"
    "    MULR  R1.w, R0.x, c[4].z;                                  \n"
    "    FLRR  R2.x, R1.w;                                          \n"
    "    MOVR  R0.xyz, R1;                                          \n"
    "    SEQR  H0.x, R2, c[0].z;                                    \n"
    "    MOVR  R1.x, R0;                                            \n"
    "    MOVRC HC.x, R2;                                            \n"
    "    ADDR  R1.z, R1.w, -R2.x;                                   \n"
    "    MULR  R0.x, R4.y, R1.z;                                    \n"
    "    MADR  R1.z, -R1, R4.y, R4.y;                               \n"
    "    MADR  R1.y, -R4, R0.w, R0.w;                               \n"
    "    MOVR  R1.x(EQ), R0.w;                                      \n"
    "    MADR  R1.z, -R1, R0.w, R0.w;                               \n"
    "    MADR  R0.x, -R0, R0.w, R0.w;                               \n"
    "    SEQR  H0.y, R2.x, c[0].w;                                  \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULX  H1.y, H0.x, H0;                                      \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R2.x, c[4];                                    \n"
    "    MULX  H1.x, H0, H0.y;                                      \n"
    "    MOVR  R1.x(GT), R0;                                        \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R2.x, c[7].x;                                  \n"
    "    MULX  H0.w, H0.x, H0.y;                                    \n"
    "    MOVR  R1.x(GT), R1.y;                                      \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R2.x, c[4].x;                                  \n"
    "    MULX  H0.z, H0.x, H0.y;                                    \n"
    "    MOVR  R1.x(GT), R1.y;                                      \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R2.x, c[7];                                    \n"
    "    MOVR  R1.x(GT), R1.z;                                      \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R1.x(GT), R0.w;                                      \n"
    "    MOVRC HC.x, R2;                                            \n"
    "    MOVR  R0.z(EQ.x), R1.y;                                    \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    MOVR  R0.z(GT.x), R1.y;                                    \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    MOVR  R0.z(GT.x), R1;                                      \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    MOVR  R0.z(GT.x), R0.w;                                    \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MOVR  R0.z(GT.x), R0.w;                                    \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R0.z(GT.x), R0.x;                                    \n"
    "    MOVRC HC.x, R2;                                            \n"
    "    MOVR  R0.y(EQ.x), R1.z;                                    \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    MOVR  R0.y(GT.x), R0.w;                                    \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    MOVR  R0.y(GT.x), R0.w;                                    \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    MOVR  R0.y(GT.x), R0.x;                                    \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MOVR  R0.y(GT.x), R1;                                      \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R0.y(GT.x), R1;                                      \n"
    "    MOVR  R1.z, R0;                                            \n"
    "    MOVR  R1.y, R0;                                            \n"
    "    ENDIF;                                                     \n"
    "    MOVR  R0.xyz, R1;                                          \n"
    "    ELSE;                                                      \n"
    "    MOVR  R2.xyz, R0;                                          \n"
    "    MOVR  R0.y, R2.x;                                          \n"
    "    MOVRC HC.x, R2;                                            \n"
    "    MOVR  R0.y(LT.x), c[0].z;                                  \n"
    "    SGTRC HC.x, R0.y, c[0].w;                                  \n"
    "    MOVR  R0.x, R0.y;                                          \n"
    "    MOVR  R0.x(GT), c[0].w;                                    \n"
    "    MOVRC HC.x, R2.y;                                          \n"
    "    MOVR  R0.yz, R2;                                           \n"
    "    IF    EQ.x;                                                \n"
    "    MOVR  R1.xyz, R0.z;                                        \n"
    "    ELSE;                                                      \n"
    "    MOVR  R1.xyz, R1;                                          \n"
    "    MADR  R1.w, -R0.y, R0.z, R0.z;                             \n"
    "    MOVR  R0.w, R0.x;                                          \n"
    "    SEQRC HC.x, R0, c[0].w;                                    \n"
    "    MOVR  R0.w(GT.x), c[0].z;                                  \n"
    "    MULR  R0.x, R0.w, c[4].z;                                  \n"
    "    FLRR  R0.w, R0.x;                                          \n"
    "    ADDR  R2.x, R0, -R0.w;                                     \n"
    "    MULR  R0.x, R0.y, R2;                                      \n"
    "    MOVRC HC.x, R0.w;                                          \n"
    "    SEQR  H0.x, R0.w, c[0].z;                                  \n"
    "    MADR  R0.y, -R2.x, R0, R0;                                 \n"
    "    MOVR  R1.x(EQ), R0.z;                                      \n"
    "    MADR  R0.x, -R0, R0.z, R0.z;                               \n"
    "    MADR  R0.y, -R0, R0.z, R0.z;                               \n"
    "    SEQR  H0.y, R0.w, c[0].w;                                  \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULX  H1.y, H0.x, H0;                                      \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R0.w, c[4];                                    \n"
    "    MULX  H1.x, H0, H0.y;                                      \n"
    "    MOVR  R1.x(GT), R0;                                        \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R0.w, c[7].x;                                  \n"
    "    MULX  H0.w, H0.x, H0.y;                                    \n"
    "    MOVR  R1.x(GT), R1.w;                                      \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R0.w, c[4].x;                                  \n"
    "    MULX  H0.z, H0.x, H0.y;                                    \n"
    "    MOVR  R1.x(GT), R1.w;                                      \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    SEQX  H0.y, H0, c[0].z;                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    SEQR  H0.y, R0.w, c[7];                                    \n"
    "    MULX  H0.x, H0, H0.y;                                      \n"
    "    MOVR  R1.x(GT), R0.y;                                      \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R1.x(GT), R0.z;                                      \n"
    "    MOVRC HC.x, R0.w;                                          \n"
    "    MOVR  R1.z(EQ.x), R1.w;                                    \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    MOVR  R1.z(GT.x), R1.w;                                    \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    MOVR  R1.z(GT.x), R0.y;                                    \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    MOVR  R1.z(GT.x), R0;                                      \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MOVR  R1.z(GT.x), R0;                                      \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R1.z(GT.x), R0.x;                                    \n"
    "    MOVRC HC.x, R0.w;                                          \n"
    "    MOVR  R1.y(EQ.x), R0;                                      \n"
    "    MOVRC HC.x, H1.y;                                          \n"
    "    MOVR  R1.y(GT.x), R0.z;                                    \n"
    "    MOVRC HC.x, H1;                                            \n"
    "    MOVR  R1.y(GT.x), R0.z;                                    \n"
    "    MOVRC HC.x, H0.w;                                          \n"
    "    MOVR  R1.y(GT.x), R0.x;                                    \n"
    "    MOVRC HC.x, H0.z;                                          \n"
    "    MOVR  R1.y(GT.x), R1.w;                                    \n"
    "    MOVRC HC.x, H0;                                            \n"
    "    MOVR  R1.y(GT.x), R1.w;                                    \n"
    "    ENDIF;                                                     \n"
    "    MOVR  R0.xyz, R1;                                          \n"
    "    ENDIF;                                                     \n"
    "    SLTR  H0.x, R0.z, c[0].z;                                  \n"
    "    MOVR  R0.w, R0.z;                                          \n"
    "    MOVRC HC.x, R0.z;                                          \n"
    "    SGTR  H0.y, R0.z, c[0].x;                                  \n"
    "    MOVR  R0.z, R0.x;                                          \n"
    "    MOVR  R0.w(LT.x), c[0].z;                                  \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULXC HC.x, H0, H0.y;                                      \n"
    "    MOVR  R0.w(GT.x), c[0].x;                                  \n"
    "    MOVRC HC.x, R0;                                            \n"
    "    SLTR  H0.x, R0, c[0].z;                                    \n"
    "    SGTR  H0.y, R0.x, c[0].x;                                  \n"
    "    MOVR  R0.x, R0.y;                                          \n"
    "    MOVR  R0.z(LT.x), c[0];                                    \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULXC HC.x, H0, H0.y;                                      \n"
    "    MOVR  R0.z(GT.x), c[0].x;                                  \n"
    "    MOVRC HC.x, R0.y;                                          \n"
    "    SLTR  H0.x, R0.y, c[0].z;                                  \n"
    "    SGTR  H0.y, R0, c[0].x;                                    \n"
    "    MULR  R0.y, R0.z, c[7].w;                                  \n"
    "    POWR  oCol.x, R0.y, c[8].x;                                \n"
    "    MULR  R0.y, R0.w, c[7].w;                                  \n"
    "    MOVR  R0.x(LT), c[0].z;                                    \n"
    "    SEQX  H0.x, H0, c[0].z;                                    \n"
    "    MULXC HC.x, H0, H0.y;                                      \n"
    "    MOVR  R0.x(GT), c[0];                                      \n"
    "    MULR  R0.x, R0, c[7].w;                                    \n"
    "    POWR  oCol.y, R0.x, c[8].x;                                \n"
    "    POWR  oCol.z, R0.y, c[8].x;                                \n"
    "    MOVR  oCol.w, c[0];                                        \n"
    "    END                                                        \n"
    "    # 498 instructions, 5 R-regs, 2 H-regs                     \n"
};

const char* 
GLWorld::m_color_correction_fragment_shader =
{
        "   float                                                                   \n"
        "   maxColor( vec3 color)                                                   \n"
        "   {                                                                       \n"
        "       float max = (color.y > color.x) ? color.y : color.x;                \n"
        "       max = (color.z > max) ? color.z : max;                              \n"
        "       return max;                                                         \n"
        "   }                                                                       \n"
        "                                                                           \n"
        "   float                                                                   \n"
        "   minColor( vec3 color)                                                   \n"
        "   {                                                                       \n"
        "       float min = (color.y < color.x) ? color.y : color.x;                \n"
        "       min = (color.z < min) ? color.z : min;                              \n"
        "       return min;                                                         \n"
        "   }                                                                       \n"
        "                                                                           \n"
        "   vec4                                                                    \n"
        "   rgb_to_hsv( vec4 rgb )                                                  \n"
        "   {                                                                       \n"
        "       vec4 hsv;                                                           \n"
        "                                                                           \n"
        "       float maximum = maxColor(rgb.xyz);                                  \n"
        "       float minimum = minColor(rgb.xyz);                                  \n"
        "       hsv.z = maximum;                                                    \n"
        "       float delta = maximum - minimum;                                    \n"
        "                                                                           \n"
        "       if (delta > 0.0001)                                                 \n"
        "       {                                                                   \n"
        "           hsv.y = delta / maximum;                                        \n"
        "                                                                           \n"
        "           if (rgb.x == maximum)                                           \n"
        "           {                                                               \n"
        "               hsv.x = (rgb.y - rgb.z) / delta;                            \n"
        "                                                                           \n"
        "               if (hsv.x < 0.0)                                            \n"
        "               {                                                           \n"
        "                   hsv.x += 6.0;                                           \n"
        "               }                                                           \n"
        "           }                                                               \n"
        "           else if (rgb.y == maximum)                                      \n"
        "           {                                                               \n"
        "               hsv.x = 2.0 + ( (rgb.z - rgb.x) / delta );                  \n"
        "           }                                                               \n"
        "           else if (rgb.z == maximum)                                      \n"
        "           {                                                               \n"
        "               hsv.x = 4.0 + ( (rgb.x - rgb.y) / delta );                  \n"
        "           }                                                               \n"
        "                                                                           \n"
        "           hsv.x /= 6.0;                                                   \n"
        "       }                                                                   \n"
        "       else                                                                \n"
        "       {                                                                   \n"
        "           hsv.x = 0.0;                                                    \n"
        "           hsv.y = 0.0;                                                    \n"
        "       }                                                                   \n"
        "                                                                           \n"
        "       hsv.w = rgb.w;                                                      \n"
        "       return hsv;                                                         \n"
        "   }                                                                       \n"
        "                                                                           \n"
        "   vec4                                                                    \n"
        "   hsv_to_rgb( vec4 hsv_in )                                               \n"
        "   {                                                                       \n"
        "       vec4 rgb;                                                           \n"
        "                                                                           \n"
        "       // rgb range 0 -255                                                 \n"
        "       if (hsv_in.y == 0.0)                                                \n"
        "       {                                                                   \n"
        "           rgb = hsv_in.zzzz;                                              \n"
        "       }                                                                   \n"
        "       else                                                                \n"
        "       {                                                                   \n"
        "           float hue = hsv_in.x;                                           \n"
        "                                                                           \n"
        "           if (hue == 1.0)                                                 \n"
        "           {                                                               \n"
        "               hue = 0.0;                                                  \n"
        "           }                                                               \n"
        "                                                                           \n"
        "           hue *= 6.0;                                                     \n"         
        "                                                                           \n"
		"           int i = int( floor(hue) );                                      \n"
		"           float f = hue - float(i);                                       \n"
        "           float w = hsv_in.z * (1.0 - hsv_in.y);                          \n"
        "           float q = hsv_in.z * ( 1.0 - (hsv_in.y * f) );                  \n"
        "           float t = hsv_in.z * ( 1.0 - (hsv_in.y * (1.0 - f) ) );         \n"
        "                                                                           \n"
        "           if (i == 0)                                                     \n"
        "           {                                                               \n"
        "               rgb.x = hsv_in.z;                                           \n"
        "               rgb.y = t;                                                  \n"
        "               rgb.z = w;                                                  \n"
        "           }                                                               \n"
        "           else if (i == 1)                                                \n"
        "           {                                                               \n"
        "               rgb.x = q;                                                  \n"
        "               rgb.y = hsv_in.z;                                           \n"
        "               rgb.z = w;                                                  \n"
        "           }                                                               \n"
        "           else if (i == 2)                                                \n"
        "           {                                                               \n"
        "               rgb.x = w;                                                  \n"
        "               rgb.y = hsv_in.z;                                           \n"
        "               rgb.z = t;                                                  \n"
        "           }                                                               \n"
        "           else if (i == 3)                                                \n"
        "           {                                                               \n"
        "               rgb.x = w;                                                  \n"
        "               rgb.y = q;                                                  \n"
        "               rgb.z = hsv_in.z;                                           \n"
        "           }                                                               \n"
        "           else if (i == 4)                                                \n"
        "           {                                                               \n"
        "               rgb.x = t;                                                  \n"
        "               rgb.y = w;                                                  \n"
        "               rgb.z = hsv_in.z;                                           \n"
        "           }                                                               \n"
        "           else if (i == 5)                                                \n"
        "           {                                                               \n"
        "               rgb.x = hsv_in.z;                                           \n"
        "               rgb.y = w;                                                  \n"
        "               rgb.z = q;                                                  \n"
        "           }                                                               \n"
        "       }                                                                   \n"
        "                                                                           \n"
        "       rgb.w = hsv_in.w;                                                   \n"
        "       return rgb;                                                         \n"
        "   }                                                                       \n"
        "                                                                           \n"
        "   vec4                                                                    \n"
        "   color_enhance( vec4 rgb_in, vec3 hsv_in, vec3 enhance )                 \n"
        "   {                                                                       \n"
        "       vec4 rgb_out;                                                       \n"
        "       vec4 hsv = rgb_to_hsv(rgb_in);                                      \n"
        "       hsv.xz += hsv_in.xz;                                                \n"
        "       hsv.y *= hsv_in.y;                                                  \n"
        "                                                                           \n"
        "       if ( hsv.x < 0.0 )                                                  \n"
        "       {                                                                   \n"
        "           hsv.x += 1.0;                                                   \n"
        "       }                                                                   \n"
        "       else if ( hsv.x > 1.0 )                                             \n"
        "       {                                                                   \n"
        "           hsv.x-= 1.0;                                                    \n"
        "       }                                                                   \n"
        "                                                                           \n"
        "       if ( hsv.y < 0.0 )                                                  \n"
        "       {                                                                   \n"
        "           hsv.y = 0.0;                                                    \n"
        "       }                                                                   \n"
        "       else if ( hsv.y > 1.0 )                                             \n"
        "       {                                                                   \n"
        "           hsv.y = 1.0;                                                    \n"
        "       }                                                                   \n"
        "                                                                           \n"
        "       // We store the brightness so that after tint we can restore it.    \n"
        "       float storeval = hsv.z;                                             \n"
        "                                                                           \n"
        "       // Now apply the tint. The old enhance.y component is unused.       \n"
        "       float tint = enhance.x; // 0 - 3600                                 \n"
        "       float str = enhance.z / 100.0; // 0 - 1                             \n"
        "                                                                           \n"
        "       if ( str > 0.0001 )                                                 \n"
        "       {                                                                   \n"
        "           vec4 tinthsv;                                                   \n"
        "           vec4 tintrgb;                                                   \n"
        "           tinthsv.x = 0.5 -  ( (tint + 900.0) / 3600.0 );                 \n"
        "                                                                           \n"
        "           if ( tinthsv.x  < 0.0 )                                         \n"
        "           {                                                               \n"
        "               tinthsv.x += 1.0;                                           \n"
        "           }                                                               \n"
        "           else if ( tinthsv.x > 1.0 )                                     \n"
        "           {                                                               \n"
        "               tinthsv.x -= 1.0;                                           \n"
        "           }                                                               \n"
        "                                                                           \n"
        "           tinthsv.y = 1.0;                                                \n"
        "           tinthsv.z = 1.0;                                                \n"
        "           tintrgb = hsv_to_rgb(tinthsv);                                  \n"
        "                                                                           \n"
        "           rgb_out = hsv_to_rgb(hsv);                                      \n"
        "                                                                           \n"
        "           float delr = 200.0 * str * tintrgb.x;                           \n"     
        "           float delg = 200.0 * str * tintrgb.y;                           \n"
        "           float delb = 200.0 * str * tintrgb.z;                           \n"
        "                                                                           \n"
        "           rgb_out.x += delr;                                              \n"
        "           rgb_out.y += delg;                                              \n"
        "           rgb_out.z += delb;                                              \n"
        "           rgb_out = clamp(rgb_out, 0.0, 255.0);							\n"    
        "                                                                           \n"
        "           hsv = rgb_to_hsv(rgb_out);                                      \n"
        "           hsv.z = storeval;                                               \n"
        "           rgb_out = hsv_to_rgb(hsv);                                      \n"
        "                                                                           \n"
        "           return rgb_out;                                                 \n"
        "       }                                                                   \n"
        "       else                                                                \n"
        "       {                                                                   \n"
        "           if ( hsv.x < 0.0 )                                              \n"
        "           {                                                               \n"
        "               hsv.x = 0.0;                                                \n"
        "           }                                                               \n"
        "                                                                           \n"
        "           if ( hsv.x > 1.0 )                                              \n"
        "           {                                                               \n"
        "               hsv.x = 1.0;                                                \n"    
        "           }                                                               \n"
        "                                                                           \n"
        "           rgb_out = hsv_to_rgb(hsv);                                      \n"
        "           return rgb_out;                                                 \n"
        "       }                                                                   \n"
        "   }                                                                       \n"
        "                                                                           \n"
        "   uniform sampler2D      base_texture;                                    \n"
        "   uniform float          brightness;                                      \n" 
        "   uniform float          gamma;                                           \n" 
        "   uniform vec3           hsv;                                             \n" 
        "   uniform vec4           color_scale;                                     \n" 
        "   uniform vec3           enhance;                                         \n" 
        "                                                                           \n"
        "   void main()                                                             \n"
        "   {                                                                       \n"
        "       vec2    base_position = gl_TexCoord[0].st;                          \n"
        "       vec4    base_color = texture2D(base_texture, base_position);        \n"
        "                                                                           \n"
        "       base_color *= 255.0;                                                \n"
        "                                                                           \n"
        "       base_color += brightness;                                           \n"
        "       base_color += color_scale;                                          \n"
        "       base_color = color_enhance(base_color, hsv, enhance);               \n"
        "       base_color = clamp(base_color, 0.0, 255.0);                         \n"
        "                                                                           \n"
        "       base_color /= 255.0;                                                \n"
		"       base_color = pow( base_color, vec4(gamma, gamma, gamma, gamma) );   \n"
        "                                                                           \n"
        "       gl_FragColor = base_color;                                          \n"
        "       gl_FragColor.w = 1.0;                                               \n"
        "   }                                                                       \n"
};

void
GLWorld::doColorCorrectorGLSL(JahLayer* image_layer)
{
    GLsizei message_length;
    char message[10000];

    static bool nvidia_cg15_error = false;

    float4          rgba;
    float4          color_scale;
    float4          enhance;
    float4          hsv;
    float           gamma;

    motionNode* sliders = getCurrentEffectLayer()->layernodes->m_node;

    color_scale.x = sliders->rx / 4.0f;
    color_scale.y = sliders->ry / 4.0f;
    color_scale.z = sliders->rz / 4.0f;


    float brightness = sliders->ty / 4.0f;  // brightness is transY.

    hsv.x = (sliders->sx - 1) / 10; // -0.5 to +0.5
    hsv.y = ( (sliders->sy - 1) * 2 / 5 ) + 1.0 ; // changed range. See sathelperslot too
    hsv.z = (sliders->sz - 1) * 100;

    gamma = sliders->Alpha / 100.0f;

    // hue offset.
    enhance.x = sliders->tx; // 0-3600
    // strength slider (NB we cant really have negative values, but deal with it later. keep centred around zero).
    enhance.z = sliders->tz; // 0-100 original, shifted
  
    color_scale.w = 0.0f;

    float height = image_layer->textureData.height;
    float width = image_layer->textureData.width;

    static GLint hsv_handle = -1;
    static GLint enhance_handle = -1;
    static GLint color_scale_handle = -1;
    static GLint brightness_handle = -1;
    static GLint gamma_handle = -1;

    static GLhandleARB shader_program_handle = 0;
    static GlslProgram* shader_program = NULL;
    static GlslShader* fragment_shader;
    static std::vector<GlslShader*> shader_vector;

    if (shader_program == NULL)
    {
        fragment_shader = new GlslShader(GL_FRAGMENT_SHADER_ARB, m_color_correction_fragment_shader);
        shader_vector.push_back(fragment_shader);
        
        shader_program = new GlslProgram(shader_vector);
        shader_program_handle = *shader_program->getGlHandle();
        
        brightness_handle = glGetUniformLocationARB(shader_program_handle, "brightness");
        hsv_handle = glGetUniformLocationARB(shader_program_handle, "hsv");
        color_scale_handle = glGetUniformLocationARB(shader_program_handle, "color_scale");
        enhance_handle = glGetUniformLocationARB(shader_program_handle, "enhance");
        gamma_handle = glGetUniformLocationARB(shader_program_handle, "gamma");

        glGetInfoLogARB(shader_program_handle, 10000, &message_length, message);

		if (message_length)
		{
			printf("Color Corrector fragment shader compilation error:\n%s\n", message);

            // Switch to CPU effect.  There is a bug in Nvidia 8.x drivers from cg 1.5
            // which forces this
            nvidia_cg15_error = true;
		}
    }

    if (nvidia_cg15_error)
    {
        getCurrentEffectLayer()->getEffectLayer()->setType(EffectInfo::COLORIZE_CPU_TYPE);
        image_layer->layername = "Colorize CPU";
    }

    glActiveTextureARB(GL_TEXTURE0_ARB);
    image_layer->bindBaseOrCompositeTexture();
    glEnable(GL_TEXTURE_2D);

    glUseProgramObjectARB(shader_program_handle);
    shader_program->setTextureHandle("base_texture", 0);

    glUniform3fARB(hsv_handle, hsv.x, hsv.y, hsv.z);
    glUniform3fARB(enhance_handle, enhance.x, enhance.y, enhance.z);
    glUniform1fARB(brightness_handle, brightness);
    glUniform1fARB(gamma_handle, gamma);
    glUniform4fARB(color_scale_handle, color_scale.x, color_scale.y, color_scale.z, color_scale.w);
    
    float texwidthratio = image_layer->textureData.texwidthratio;
    float texheightratio = image_layer->textureData.texheightratio;

    glBegin(GL_QUADS);
    {
        glMultiTexCoord4fARB(GL_TEXTURE0_ARB, 0.0, 0.0, 0.0, 1.0); 
        glVertex2f( -width / 2.0, -height / 2.0);

        glMultiTexCoord4fARB(GL_TEXTURE0_ARB, texwidthratio, 0.0, 0.0, 1.0);
        glVertex2f( width / 2.0, -height / 2.0);

        glMultiTexCoord4fARB(GL_TEXTURE0_ARB, texwidthratio, texheightratio, 0.0, 1.0);
        glVertex2f( width / 2.0, height / 2.0);

        glMultiTexCoord4fARB(GL_TEXTURE0_ARB, 0.0, texheightratio, 0.0, 1.0); 
        glVertex2f( -width / 2.0, height / 2.0);
    }
    glEnd(); //GL_QUADS

    ////////////////////////////////////////////
    //grab a copy of the resulting image into the buffer

    glBindTexture(GL_TEXTURE_2D, image_layer->getTextureComposite().texID );

    int texture_lower_left_x = jahstd::Max<int>(0, int( width - core->m_renderwidth )) / 2;
    int texture_lower_left_y = jahstd::Max<int>(0, int( height - core->m_renderheight )) / 2;
    int screen_lower_left_x = jahstd::Max<int>( int(core->m_renderwidth - width ), 0 ) / 2;
    int screen_lower_left_y = jahstd::Max<int>( int(core->m_renderheight - height ), 0 ) / 2;
    int copy_width = jahstd::Min<int>(int( width ), int( core->m_renderwidth ));
    int copy_height = jahstd::Min<int>(int( height ), int( core->m_renderheight ));

    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 
        texture_lower_left_x,
        texture_lower_left_y,
        screen_lower_left_x,
        screen_lower_left_y, 
        copy_width, 
        copy_height );

    glUseProgramObjectARB(0);
}

