view src/windows/native/sun/java2d/d3d/D3DShaderGen.c @ 8962:c8c4aef922ff

8029628: Many graphic artifacts Reviewed-by: prr, bae
author vadim
date Fri, 13 Dec 2013 11:49:26 +0400
parents 3efc003bf097
children
line wrap: on
line source
/*
 * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/**
 * This file contains a standalone program that is used to generate the
 * D3DShaders.h file.  The program invokes the fxc (D3D Shader Compiler)
 * utility, which is part of the DirectX 9/10 SDK.  Since most JDK
 * developers (other than some Java 2D engineers) do not have the full DXSDK
 * installed, and since we do not want to make the JDK build process
 * dependent on the full DXSDK installation, we have chosen not to make
 * this shader compilation step part of the build process.  Instead, it is
 * only necessary to compile and run this program when changes need to be
 * made to the shader code contained within.  Typically, this only happens
 * on an as-needed basis by someone familiar with the D3D pipeline.  Running
 * this program is fairly straightforward:
 *
 *   % rm D3DShaders.h
 *   % cl D3DShaderGen.c
 *   % D3DShaderGen.exe
 *
 * (And don't forget to putback the updated D3DShaders.h file!)
 */

#include <stdio.h>
#include <process.h>
#include <Windows.h>

static FILE *fpHeader = NULL;
static char *strHeaderFile = "D3DShaders.h";

/** Evaluates to true if the given bit is set on the local flags variable. */
#define IS_SET(flagbit) \
    (((flags) & (flagbit)) != 0)

// REMIND
//#define J2dTraceLn(a, b) fprintf(stderr, "%s\n", b);
//#define J2dTraceLn1(a, b, c) fprintf(stderr, b, c);
#define J2dTraceLn(a, b)
#define J2dTraceLn1(a, b, c)

/************************* General shader support ***************************/

static void
D3DShaderGen_WriteShader(char *source, char *target, char *name, int flags)
{
    FILE *fpTmp;
    char varname[50];
    char *args[8];
    int val;

    // write source to tmp.hlsl
    fpTmp = fopen("tmp.hlsl", "w");
    fprintf(fpTmp, "%s\n", source);
    fclose(fpTmp);

    {
        PROCESS_INFORMATION pi;
        STARTUPINFO si;
        char pargs[300];
        sprintf(pargs,
                "c:\\progra~1\\mi5889~1\\utilit~1\\bin\\x86\\fxc.exe "
                "/T %s /Vn %s%d /Fh tmp.h tmp.hlsl",
                // uncomment the following line to generate debug
                // info in the shader header file (may be useful
                // for testing/debuggging purposes, but it nearly
                // doubles the size of the header file and compiled
                // shader programs - off for production builds)
                //"/Zi /T %s /Vn %s%d /Fh tmp.h tmp.hlsl",
                target, name, flags);
        fprintf(stderr, "%s\n", pargs);
        memset(&si, 0, sizeof(si));
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESTDHANDLES;
        //si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
        //fprintf(stderr, "%s\n", pargs);
        val = CreateProcess(0, pargs, 0, 0, TRUE,
                            CREATE_NO_WINDOW, NULL, NULL, &si, &pi);

        {
            DWORD code;
            do {
                GetExitCodeProcess(pi.hProcess, &code);
                //fprintf(stderr, "waiting...");
                Sleep(100);
            } while (code == STILL_ACTIVE);

            if (code != 0) {
                fprintf(stderr, "fxc failed for %s%d\n", name, flags);
            }
        }

        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }

    // append tmp.h to D3DShaders.h
    {
        int ch;
        fpTmp = fopen("tmp.h", "r");
        while ((ch = fgetc(fpTmp)) != EOF) {
            fputc(ch, fpHeader);
        }
        fclose(fpTmp);
    }
}

static void
D3DShaderGen_WritePixelShader(char *source, char *name, int flags)
{
    D3DShaderGen_WriteShader(source, "ps_2_0", name, flags);
}

#define MULTI_GRAD_CYCLE_METHOD (3 << 0)
/** Extracts the CycleMethod enum value from the given flags variable. */
#define EXTRACT_CYCLE_METHOD(flags) \
    ((flags) & MULTI_GRAD_CYCLE_METHOD)

static void
D3DShaderGen_WriteShaderArray(char *name, int num)
{
    char array[5000];
    char elem[30];
    int i;

    sprintf(array, "const DWORD *%sShaders[] =\n{\n", name);
    for (i = 0; i < num; i++) {
        if (num == 32 && EXTRACT_CYCLE_METHOD(i) == 3) {
            // REMIND: what a hack!
            sprintf(elem, "    NULL,\n");
        } else {
            sprintf(elem, "    %s%d,\n", name, i);
        }
        strcat(array, elem);
    }
    strcat(array, "};\n");

    // append to D3DShaders.h
    fprintf(fpHeader, "%s\n", array);
}

/**************************** ConvolveOp support ****************************/

static const char *convolveShaderSource =
    // image to be convolved
    "sampler2D baseImage   : register(s0);"
    // image edge limits:
    //   imgEdge.xy = imgMin.xy (anything < will be treated as edge case)
    //   imgEdge.zw = imgMax.xy (anything > will be treated as edge case)
    "float4 imgEdge        : register(c0);"
    // value for each location in the convolution kernel:
    //   kernelVals[i].x = offsetX[i]
    //   kernelVals[i].y = offsetY[i]
    //   kernelVals[i].z = kernel[i]
    "float3 kernelVals[%d] : register(c1);"
    ""
    "void main(in float2 tc : TEXCOORD0,"
    "          inout float4 color : COLOR0)"
    "{"
    "    float4 sum = imgEdge - tc.xyxy;"
    ""
    "    if (sum.x > 0 || sum.y > 0 || sum.z < 0 || sum.w < 0) {"
             // (placeholder for edge condition code)
    "        color = %s;"
    "    } else {"
    "        int i;"
    "        sum = float4(0, 0, 0, 0);"
    "        for (i = 0; i < %d; i++) {"
    "            sum +="
    "                kernelVals[i].z *"
    "                tex2D(baseImage, tc + kernelVals[i].xy);"
    "        }"
             // modulate with current color in order to apply extra alpha
    "        color *= sum;"
    "    }"
    ""
    "}";

/**
 * Flags that can be bitwise-or'ed together to control how the shader
 * source code is generated.
 */
#define CONVOLVE_EDGE_ZERO_FILL (1 << 0)
#define CONVOLVE_5X5            (1 << 1)
#define MAX_CONVOLVE            (1 << 2)

static void
D3DShaderGen_GenerateConvolveShader(int flags)
{
    int kernelMax = IS_SET(CONVOLVE_5X5) ? 25 : 9;
    char *edge;
    char finalSource[2000];

    J2dTraceLn1(J2D_TRACE_INFO,
                "D3DShaderGen_GenerateConvolveShader: flags=%d",
                flags);

    if (IS_SET(CONVOLVE_EDGE_ZERO_FILL)) {
        // EDGE_ZERO_FILL: fill in zero at the edges
        edge = "float4(0, 0, 0, 0)";
    } else {
        // EDGE_NO_OP: use the source pixel color at the edges
        edge = "tex2D(baseImage, tc)";
    }

    // compose the final source code string from the various pieces
    sprintf(finalSource, convolveShaderSource,
            kernelMax, edge, kernelMax);

    D3DShaderGen_WritePixelShader(finalSource, "convolve", flags);
}

/**************************** RescaleOp support *****************************/

static const char *rescaleShaderSource =
    // image to be rescaled
    "sampler2D baseImage : register(s0);"
    // vector containing scale factors
    "float4 scaleFactors : register(c0);"
    // vector containing offsets
    "float4 offsets      : register(c1);"
    ""
    "void main(in float2 tc : TEXCOORD0,"
    "          inout float4 color : COLOR0)"
    "{"
    "    float4 srcColor = tex2D(baseImage, tc);"
    ""
         // (placeholder for un-premult code)
    "    %s"
    ""
         // rescale source value
    "    float4 result = (srcColor * scaleFactors) + offsets;"
    ""
         // (placeholder for re-premult code)
    "    %s"
    ""
         // modulate with current color in order to apply extra alpha
    "    color *= result;"
    "}";

/**
 * Flags that can be bitwise-or'ed together to control how the shader
 * source code is generated.
 */
#define RESCALE_NON_PREMULT (1 << 0)
#define MAX_RESCALE         (1 << 1)

static void
D3DShaderGen_GenerateRescaleShader(int flags)
{
    char *preRescale = "";
    char *postRescale = "";
    char finalSource[2000];

    J2dTraceLn1(J2D_TRACE_INFO,
                "D3DShaderGen_GenerateRescaleShader: flags=%d",
                flags);

    if (IS_SET(RESCALE_NON_PREMULT)) {
        preRescale  = "srcColor.rgb /= srcColor.a;";
        postRescale = "result.rgb *= result.a;";
    }

    // compose the final source code string from the various pieces
    sprintf(finalSource, rescaleShaderSource,
            preRescale, postRescale);

    D3DShaderGen_WritePixelShader(finalSource, "rescale", flags);
}

/**************************** LookupOp support ******************************/

static const char *lookupShaderSource =
    // source image (bound to texture unit 0)
    "sampler2D baseImage   : register(s0);"
    // lookup table (bound to texture unit 1)
    "sampler2D lookupTable : register(s1);"
    // offset subtracted from source index prior to lookup step
    "float4 offset         : register(c0);"
    ""
    "void main(in float2 tc : TEXCOORD0,"
    "          inout float4 color : COLOR0)"
    "{"
    "    float4 srcColor = tex2D(baseImage, tc);"
         // (placeholder for un-premult code)
    "    %s"
         // subtract offset from original index
    "    float4 srcIndex = srcColor - offset;"
         // use source value as input to lookup table (note that
         // "v" texcoords are hardcoded to hit texel centers of
         // each row/band in texture)
    "    float4 result;"
    "    result.r = tex2D(lookupTable, float2(srcIndex.r, 0.125)).r;"
    "    result.g = tex2D(lookupTable, float2(srcIndex.g, 0.375)).r;"
    "    result.b = tex2D(lookupTable, float2(srcIndex.b, 0.625)).r;"
         // (placeholder for alpha store code)
    "    %s"
         // (placeholder for re-premult code)
    "    %s"
         // modulate with current color in order to apply extra alpha
    "    color *= result;"
    "}";

/**
 * Flags that can be bitwise-or'ed together to control how the shader
 * source code is generated.
 */
#define LOOKUP_USE_SRC_ALPHA (1 << 0)
#define LOOKUP_NON_PREMULT   (1 << 1)
#define MAX_LOOKUP           (1 << 2)

static void
D3DShaderGen_GenerateLookupShader(int flags)
{
    char *alpha;
    char *preLookup = "";
    char *postLookup = "";
    char finalSource[2000];

    J2dTraceLn1(J2D_TRACE_INFO,
                "D3DShaderGen_GenerateLookupShader: flags=%d",
                flags);

    if (IS_SET(LOOKUP_USE_SRC_ALPHA)) {
        // when numComps is 1 or 3, the alpha is not looked up in the table;
        // just keep the alpha from the source fragment
        alpha = "result.a = srcColor.a;";
    } else {
        // when numComps is 4, the alpha is looked up in the table, just
        // like the other color components from the source fragment
        alpha = "result.a = tex2D(lookupTable, float2(srcIndex.a, 0.875)).r;";
    }
    if (IS_SET(LOOKUP_NON_PREMULT)) {
        preLookup  = "srcColor.rgb /= srcColor.a;";
        postLookup = "result.rgb *= result.a;";
    }

    // compose the final source code string from the various pieces
    sprintf(finalSource, lookupShaderSource,
            preLookup, alpha, postLookup);

    D3DShaderGen_WritePixelShader(finalSource, "lookup", flags);
}

/************************* GradientPaint support ****************************/

/*
 * To simplify the code and to make it easier to upload a number of
 * uniform values at once, we pack a bunch of scalar (float) values
 * into a single float3 below.  Here's how the values are related:
 *
 *   params.x = p0
 *   params.y = p1
 *   params.z = p3
 */
static const char *basicGradientShaderSource =
    "float3 params : register (c0);"
    "float4 color1 : register (c1);"
    "float4 color2 : register (c2);"
    // (placeholder for mask variable)
    "%s"
    ""
    // (placeholder for mask texcoord input)
    "void main(%s"
    "          in float4 winCoord : TEXCOORD%d,"
    "          inout float4 color : COLOR0)"
    "{"
    "    float3 fragCoord = float3(winCoord.x, winCoord.y, 1.0);"
    "    float dist = dot(params.xyz, fragCoord);"
    ""
         // the setup code for p0/p1/p3 translates/scales to hit texel
         // centers (at 0.25 and 0.75) because it is needed for the
         // original/fast texture-based implementation, but it is not
         // desirable for this shader-based implementation, so we
         // re-transform the value here...
    "    dist = (dist - 0.25) * 2.0;"
    ""
    "    float fraction;"
         // (placeholder for cycle code)
    "    %s"
    ""
    "    float4 result = lerp(color1, color2, fraction);"
    ""
         // (placeholder for mask modulation code)
    "    %s"
    ""
         // modulate with current color in order to apply extra alpha
    "    color *= result;"
    "}";

/**
 * Flags that can be bitwise-or'ed together to control how the shader
 * source code is generated.
 */
#define BASIC_GRAD_IS_CYCLIC (1 << 0)
#define BASIC_GRAD_USE_MASK  (1 << 1)
#define MAX_BASIC_GRAD       (1 << 2)

static void
D3DShaderGen_GenerateBasicGradShader(int flags)
{
    int colorSampler = IS_SET(BASIC_GRAD_USE_MASK) ? 1 : 0;
    char *cycleCode;
    char *maskVars = "";
    char *maskInput = "";
    char *maskCode = "";
    char finalSource[3000];

    J2dTraceLn1(J2D_TRACE_INFO,
                "D3DShaderGen_GenerateBasicGradShader",
                flags);

    if (IS_SET(BASIC_GRAD_IS_CYCLIC)) {
        cycleCode =
            "fraction = 1.0 - (abs(frac(dist * 0.5) - 0.5) * 2.0);";
    } else {
        cycleCode =
            "fraction = clamp(dist, 0.0, 1.0);";
    }

    if (IS_SET(BASIC_GRAD_USE_MASK)) {
        /*
         * This code modulates the calculated result color with the
         * corresponding alpha value from the alpha mask texture active
         * on texture unit 0.  Only needed when useMask is true (i.e., only
         * for MaskFill operations).
         */
        maskVars = "sampler2D mask : register(s0);";
        maskInput = "in float4 maskCoord : TEXCOORD0,";
        maskCode = "result *= tex2D(mask, maskCoord.xy).a;";
    }

    // compose the final source code string from the various pieces
    sprintf(finalSource, basicGradientShaderSource,
            maskVars, maskInput, colorSampler, cycleCode, maskCode);

    D3DShaderGen_WritePixelShader(finalSource, "grad", flags);
}

/****************** Shared MultipleGradientPaint support ********************/

/**
 * These constants are identical to those defined in the
 * MultipleGradientPaint.CycleMethod enum; they are copied here for
 * convenience (ideally we would pull them directly from the Java level,
 * but that entails more hassle than it is worth).
 */
#define CYCLE_NONE    0
#define CYCLE_REFLECT 1
#define CYCLE_REPEAT  2

/**
 * The following constants are flags that can be bitwise-or'ed together
 * to control how the MultipleGradientPaint shader source code is generated:
 *
 *   MULTI_GRAD_CYCLE_METHOD
 *     Placeholder for the CycleMethod enum constant.
 *
 *   MULTI_GRAD_LARGE
 *     If set, use the (slower) shader that supports a larger number of
 *     gradient colors; otherwise, use the optimized codepath.  See
 *     the MAX_FRACTIONS_SMALL/LARGE constants below for more details.
 *
 *   MULTI_GRAD_USE_MASK
 *     If set, apply the alpha mask value from texture unit 1 to the
 *     final color result (only used in the MaskFill case).
 *
 *   MULTI_GRAD_LINEAR_RGB
 *     If set, convert the linear RGB result back into the sRGB color space.
 */
//#define MULTI_GRAD_CYCLE_METHOD (3 << 0)
#define MULTI_GRAD_LARGE        (1 << 2)
#define MULTI_GRAD_USE_MASK     (1 << 3)
#define MULTI_GRAD_LINEAR_RGB   (1 << 4)

// REMIND
#define MAX_MULTI_GRAD     (1 << 5)

/** Extracts the CycleMethod enum value from the given flags variable. */
//#define EXTRACT_CYCLE_METHOD(flags) \
//    ((flags) & MULTI_GRAD_CYCLE_METHOD)

/**
 * The maximum number of gradient "stops" supported by the fragment shader
 * and related code.  When the MULTI_GRAD_LARGE flag is set, we will use
 * MAX_FRACTIONS_LARGE; otherwise, we use MAX_FRACTIONS_SMALL.  By having
 * two separate values, we can have one highly optimized shader (SMALL) that
 * supports only a few fractions/colors, and then another, less optimal
 * shader that supports more stops.
 */
#define MAX_FRACTIONS 8
#define MAX_FRACTIONS_LARGE MAX_FRACTIONS
#define MAX_FRACTIONS_SMALL 4

/**
 * The maximum number of gradient colors supported by all of the gradient
 * fragment shaders.  Note that this value must be a power of two, as it
 * determines the size of the 1D texture created below.  It also must be
 * greater than or equal to MAX_FRACTIONS (there is no strict requirement
 * that the two values be equal).
 */
#define MAX_COLORS 16

static const char *multiGradientShaderSource =
    // gradient texture size (in texels)
    "#define TEXTURE_SIZE  %d\n"
    // maximum number of fractions/colors supported by this shader
    "#define MAX_FRACTIONS %d\n"
    // size of a single texel
    "#define FULL_TEXEL    (1.0 / float(TEXTURE_SIZE))\n"
    // size of half of a single texel
    "#define HALF_TEXEL    (FULL_TEXEL / 2.0)\n"
    // texture containing the gradient colors
    "sampler2D colors                : register (s%d);"
    // array of gradient stops/fractions and corresponding scale factors
    //   fractions[i].x = gradientStop[i]
    //   fractions[i].y = scaleFactor[i]
    "float2 fractions[MAX_FRACTIONS] : register (c0);"
    // (placeholder for mask variable)
    "%s"
    // (placeholder for Linear/RadialGP-specific variables)
    "%s"
    ""
    // (placeholder for mask texcoord input)
    "void main(%s"
    "          in float4 winCoord : TEXCOORD%d,"
    "          inout float4 color : COLOR0)"
    "{"
    "    float dist;"
         // (placeholder for Linear/RadialGradientPaint-specific code)
    "    %s"
    ""
    "    float4 result;"
         // (placeholder for CycleMethod-specific code)
    "    %s"
    ""
         // (placeholder for ColorSpace conversion code)
    "    %s"
    ""
         // (placeholder for mask modulation code)
    "    %s"
    ""
         // modulate with current color in order to apply extra alpha
    "    color *= result;"
    "}";

/*
 * Note: An earlier version of this code would simply calculate a single
 * texcoord:
 *     "tc = HALF_TEXEL + (FULL_TEXEL * relFraction);"
 * and then use that value to do a single texture lookup, taking advantage
 * of the LINEAR texture filtering mode which in theory will do the
 * appropriate linear interpolation between adjacent texels, like this:
 *     "float4 result = tex2D(colors, float2(tc, 0.5));"
 *
 * The problem with that approach is that on certain hardware (from ATI,
 * notably) the LINEAR texture fetch unit has low precision, and would
 * for instance only produce 64 distinct grayscales between white and black,
 * instead of the expected 256.  The visual banding caused by this issue
 * is severe enough to likely cause complaints from developers, so we have
 * devised a new approach below that instead manually fetches the two
 * relevant neighboring texels and then performs the linear interpolation
 * using the lerp() instruction (which does not suffer from the precision
 * issues of the fixed-function texture filtering unit).  This new approach
 * requires a few more instructions and is therefore slightly slower than
 * the old approach (not more than 10% or so).
 */
static const char *texCoordCalcCode =
    "int i;"
    "float relFraction = 0.0;"
    "for (i = 0; i < MAX_FRACTIONS-1; i++) {"
    "    relFraction +="
    "        clamp((dist - fractions[i].x) * fractions[i].y, 0.0, 1.0);"
    "}"
    // we offset by half a texel so that we find the linearly interpolated
    // color between the two texel centers of interest
    "float intPart = floor(relFraction);"
    "float tc1 = HALF_TEXEL + (FULL_TEXEL * intPart);"
    "float tc2 = HALF_TEXEL + (FULL_TEXEL * (intPart + 1.0));"
    "float4 clr1 = tex2D(colors, float2(tc1, 0.5));"
    "float4 clr2 = tex2D(colors, float2(tc2, 0.5));"
    "result = lerp(clr1, clr2, frac(relFraction));";

/** Code for NO_CYCLE that gets plugged into the CycleMethod placeholder. */
static const char *noCycleCode =
    "if (dist <= 0.0) {"
    "    result = tex2D(colors, float2(0.0, 0.5));"
    "} else if (dist >= 1.0) {"
    "    result = tex2D(colors, float2(1.0, 0.5));"
    "} else {"
         // (placeholder for texcoord calculation)
    "    %s"
    "}";

/** Code for REFLECT that gets plugged into the CycleMethod placeholder. */
static const char *reflectCode =
    "dist = 1.0 - (abs(frac(dist * 0.5) - 0.5) * 2.0);"
    // (placeholder for texcoord calculation)
    "%s";

/** Code for REPEAT that gets plugged into the CycleMethod placeholder. */
static const char *repeatCode =
    "dist = frac(dist);"
    // (placeholder for texcoord calculation)
    "%s";

static void
D3DShaderGen_GenerateMultiGradShader(int flags, char *name,
                                     char *paintVars, char *distCode)
{
    char *maskVars = "";
    char *maskInput = "";
    char *maskCode = "";
    char *colorSpaceCode = "";
    char cycleCode[1500];
    char finalSource[3000];
    int colorSampler = IS_SET(MULTI_GRAD_USE_MASK) ? 1 : 0;
    int cycleMethod = EXTRACT_CYCLE_METHOD(flags);
    int maxFractions = IS_SET(MULTI_GRAD_LARGE) ?
        MAX_FRACTIONS_LARGE : MAX_FRACTIONS_SMALL;

    J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_CreateMultiGradProgram");

    if (IS_SET(MULTI_GRAD_USE_MASK)) {
        /*
         * This code modulates the calculated result color with the
         * corresponding alpha value from the alpha mask texture active
         * on texture unit 0.  Only needed when useMask is true (i.e., only
         * for MaskFill operations).
         */
        maskVars = "sampler2D mask : register(s0);";
        maskInput = "in float4 maskCoord : TEXCOORD0,";
        maskCode = "result *= tex2D(mask, maskCoord.xy).a;";
    }

    if (IS_SET(MULTI_GRAD_LINEAR_RGB)) {
        /*
         * This code converts a single pixel in linear RGB space back
         * into sRGB (note: this code was adapted from the
         * MultipleGradientPaintContext.convertLinearRGBtoSRGB() method).
         */
        colorSpaceCode =
            "result.rgb = 1.055 * pow(result.rgb, 0.416667) - 0.055;";
    }

    if (cycleMethod == CYCLE_NONE) {
        sprintf(cycleCode, noCycleCode, texCoordCalcCode);
    } else if (cycleMethod == CYCLE_REFLECT) {
        sprintf(cycleCode, reflectCode, texCoordCalcCode);
    } else { // (cycleMethod == CYCLE_REPEAT)
        sprintf(cycleCode, repeatCode, texCoordCalcCode);
    }

    // compose the final source code string from the various pieces
    sprintf(finalSource, multiGradientShaderSource,
            MAX_COLORS, maxFractions, colorSampler,
            maskVars, paintVars, maskInput, colorSampler,
            distCode, cycleCode, colorSpaceCode, maskCode);

    D3DShaderGen_WritePixelShader(finalSource, name, flags);
}

/********************** LinearGradientPaint support *************************/

static void
D3DShaderGen_GenerateLinearGradShader(int flags)
{
    char *paintVars;
    char *distCode;

    J2dTraceLn1(J2D_TRACE_INFO,
                "D3DShaderGen_GenerateLinearGradShader",
                flags);

    /*
     * To simplify the code and to make it easier to upload a number of
     * uniform values at once, we pack a bunch of scalar (float) values
     * into a single float3 below.  Here's how the values are related:
     *
     *   params.x = p0
     *   params.y = p1
     *   params.z = p3
     */
    paintVars =
        "float3 params : register(c16);";
    distCode =
        "float3 fragCoord = float3(winCoord.x, winCoord.y, 1.0);"
        "dist = dot(params.xyz, fragCoord);";

    D3DShaderGen_GenerateMultiGradShader(flags, "linear",
                                         paintVars, distCode);
}

/********************** RadialGradientPaint support *************************/

static void
D3DShaderGen_GenerateRadialGradShader(int flags)
{
    char *paintVars;
    char *distCode;

    J2dTraceLn1(J2D_TRACE_INFO,
                "D3DShaderGen_GenerateRadialGradShader",
                flags);

    /*
     * To simplify the code and to make it easier to upload a number of
     * uniform values at once, we pack a bunch of scalar (float) values
     * into float3 values below.  Here's how the values are related:
     *
     *   m0.x = m00
     *   m0.y = m01
     *   m0.z = m02
     *
     *   m1.x = m10
     *   m1.y = m11
     *   m1.z = m12
     *
     *   precalc.x = focusX
     *   precalc.y = 1.0 - (focusX * focusX)
     *   precalc.z = 1.0 / precalc.z
     */
    paintVars =
        "float3 m0      : register(c16);"
        "float3 m1      : register(c17);"
        "float3 precalc : register(c18);";

    /*
     * The following code is derived from Daniel Rice's whitepaper on
     * radial gradient performance (attached to the bug report for 6521533).
     * Refer to that document as well as the setup code in the Java-level
     * BufferedPaints.setRadialGradientPaint() method for more details.
     */
    distCode =
        "float3 fragCoord = float3(winCoord.x, winCoord.y, 1.0);"
        "float x = dot(fragCoord, m0);"
        "float y = dot(fragCoord, m1);"
        "float xfx = x - precalc.x;"
        "dist = (precalc.x*xfx + sqrt(xfx*xfx + y*y*precalc.y))*precalc.z;";

    D3DShaderGen_GenerateMultiGradShader(flags, "radial",
                                         paintVars, distCode);
}

/*************************** LCD text support *******************************/

// REMIND: Shader uses texture addressing operations in a dependency chain
//         that is too complex for the target shader model (ps_2_0) to handle
//         (ugh, I guess we can either require ps_3_0 or just use
//         the slower pow intrinsic)
#define POW_LUT 0

static const char *lcdTextShaderSource =
    "float3 srcAdj         : register(c0);"
    "sampler2D glyphTex    : register(s0);"
    "sampler2D dstTex      : register(s1);"
#if POW_LUT
    "sampler3D invgammaTex : register(s2);"
    "sampler3D gammaTex    : register(s3);"
#else
    "float3 invgamma       : register(c1);"
    "float3 gamma          : register(c2);"
#endif
    ""
    "void main(in float2 tc0 : TEXCOORD0,"
    "          in float2 tc1 : TEXCOORD1,"
    "          inout float4 color : COLOR0)"
    "{"
         // load the RGB value from the glyph image at the current texcoord
    "    float3 glyphClr = tex2D(glyphTex, tc0).rgb;"
    "    if (!any(glyphClr)) {"
             // zero coverage, so skip this fragment
    "        discard;"
    "    }"
         // load the RGB value from the corresponding destination pixel
    "    float3 dstClr = tex2D(dstTex, tc1).rgb;"
         // gamma adjust the dest color using the invgamma LUT
#if POW_LUT
    "    float3 dstAdj = tex3D(invgammaTex, dstClr).rgb;"
#else
    "    float3 dstAdj = pow(dstClr, invgamma);"
#endif
         // linearly interpolate the three color values
    "    float3 result = lerp(dstAdj, srcAdj, glyphClr);"
         // gamma re-adjust the resulting color (alpha is always set to 1.0)
#if POW_LUT
    "    color = float4(tex3D(gammaTex, result).rgb, 1.0);"
#else
    "    color = float4(pow(result, gamma), 1.0);"
#endif
    "}";

static void
D3DShaderGen_GenerateLCDTextShader()
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DShaderGen_GenerateLCDTextShader");

    D3DShaderGen_WritePixelShader((char *)lcdTextShaderSource, "lcdtext", 0);
}

/*************************** AA support *******************************/

/*
 * This shader fills the space between an outer and inner parallelogram.
 * It can be used to draw an outline by specifying both inner and outer
 * values.  It fills pixels by estimating what portion falls inside the
 * outer shape, and subtracting an estimate of what portion falls inside
 * the inner shape.  Specifying both inner and outer values produces a
 * standard "wide outline".  Specifying an inner shape that falls far
 * outside the outer shape allows the same shader to fill the outer
 * shape entirely since pixels that fall within the outer shape are never
 * inside the inner shape and so they are filled based solely on their
 * coverage of the outer shape.
 *
 * The setup code renders this shader over the bounds of the outer
 * shape (or the only shape in the case of a fill operation) and
 * sets the texture 0 coordinates so that 0,0=>0,1=>1,1=>1,0 in those
 * texture coordinates map to the four corners of the parallelogram.
 * Similarly the texture 1 coordinates map the inner shape to the
 * unit square as well, but in a different coordinate system.
 *
 * When viewed in the texture coordinate systems the parallelograms
 * we are filling are unit squares, but the pixels have then become
 * tiny parallelograms themselves.  Both of the texture coordinate
 * systems are affine transforms so the rate of change in X and Y
 * of the texture coordinates are essentially constants and happen
 * to correspond to the size and direction of the slanted sides of
 * the distorted pixels relative to the "square mapped" boundary
 * of the parallelograms.
 *
 * The shader uses the ddx() and ddy() functions to measure the "rate
 * of change" of these texture coordinates and thus gets an accurate
 * measure of the size and shape of a pixel relative to the two
 * parallelograms.  It then uses the bounds of the size and shape
 * of a pixel to intersect with the unit square to estimate the
 * coverage of the pixel.  Unfortunately, without a lot more work
 * to calculate the exact area of intersection between a unit
 * square (the original parallelogram) and a parallelogram (the
 * distorted pixel), this shader only approximates the pixel
 * coverage, but emperically the estimate is very useful and
 * produces visually pleasing results, if not theoretically accurate.
 */
static const char *aaShaderSource =
    "void main(in float2 tco : TEXCOORD0,"
    "          in float2 tci : TEXCOORD1,"
    "          inout float4 color : COLOR0)"
    "{"
    // Calculate the vectors for the "legs" of the pixel parallelogram
    // for the outer parallelogram.
    "    float2 oleg1 = ddx(tco);"
    "    float2 oleg2 = ddy(tco);"
    // Calculate the bounds of the distorted pixel parallelogram.
    "    float2 omin = min(tco, tco+oleg1);"
    "    omin = min(omin, tco+oleg2);"
    "    omin = min(omin, tco+oleg1+oleg2);"
    "    float2 omax = max(tco, tco+oleg1);"
    "    omax = max(omax, tco+oleg2);"
    "    omax = max(omax, tco+oleg1+oleg2);"
    // Calculate the vectors for the "legs" of the pixel parallelogram
    // for the inner parallelogram.
    "    float2 ileg1 = ddx(tci);"
    "    float2 ileg2 = ddy(tci);"
    // Calculate the bounds of the distorted pixel parallelogram.
    "    float2 imin = min(tci, tci+ileg1);"
    "    imin = min(imin, tci+ileg2);"
    "    imin = min(imin, tci+ileg1+ileg2);"
    "    float2 imax = max(tci, tci+ileg1);"
    "    imax = max(imax, tci+ileg2);"
    "    imax = max(imax, tci+ileg1+ileg2);"
    // Clamp the bounds of the parallelograms to the unit square to
    // estimate the intersection of the pixel parallelogram with
    // the unit square.  The ratio of the 2 rectangle areas is a
    // reasonable estimate of the proportion of coverage.
    "    float2 o1 = clamp(omin, 0.0, 1.0);"
    "    float2 o2 = clamp(omax, 0.0, 1.0);"
    "    float oint = (o2.y-o1.y)*(o2.x-o1.x);"
    "    float oarea = (omax.y-omin.y)*(omax.x-omin.x);"
    "    float2 i1 = clamp(imin, 0.0, 1.0);"
    "    float2 i2 = clamp(imax, 0.0, 1.0);"
    "    float iint = (i2.y-i1.y)*(i2.x-i1.x);"
    "    float iarea = (imax.y-imin.y)*(imax.x-imin.x);"
    // Proportion of pixel in outer shape minus the proportion
    // of pixel in the inner shape == the coverage of the pixel
    // in the area between the two.
    "    float coverage = oint/oarea - iint / iarea;"
    "    color *= coverage;"
    "}";

static void
D3DShaderGen_GenerateAAParallelogramShader()
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DShaderGen_GenerateAAParallelogramShader");

    D3DShaderGen_WriteShader((char *)aaShaderSource, "ps_2_a", "aapgram", 0);
}

/**************************** Main entrypoint *******************************/

static void
D3DShaderGen_GenerateAllShaders()
{
    int i;

#if 1
    // Generate BufferedImageOp shaders
    for (i = 0; i < MAX_RESCALE; i++) {
        D3DShaderGen_GenerateRescaleShader(i);
    }
    D3DShaderGen_WriteShaderArray("rescale", MAX_RESCALE);
    for (i = 0; i < MAX_CONVOLVE; i++) {
        D3DShaderGen_GenerateConvolveShader(i);
    }
    D3DShaderGen_WriteShaderArray("convolve", MAX_CONVOLVE);
    for (i = 0; i < MAX_LOOKUP; i++) {
        D3DShaderGen_GenerateLookupShader(i);
    }
    D3DShaderGen_WriteShaderArray("lookup", MAX_LOOKUP);

    // Generate Paint shaders
    for (i = 0; i < MAX_BASIC_GRAD; i++) {
        D3DShaderGen_GenerateBasicGradShader(i);
    }
    D3DShaderGen_WriteShaderArray("grad", MAX_BASIC_GRAD);
    for (i = 0; i < MAX_MULTI_GRAD; i++) {
        if (EXTRACT_CYCLE_METHOD(i) == 3) continue; // REMIND
        D3DShaderGen_GenerateLinearGradShader(i);
    }
    D3DShaderGen_WriteShaderArray("linear", MAX_MULTI_GRAD);
    for (i = 0; i < MAX_MULTI_GRAD; i++) {
        if (EXTRACT_CYCLE_METHOD(i) == 3) continue; // REMIND
        D3DShaderGen_GenerateRadialGradShader(i);
    }
    D3DShaderGen_WriteShaderArray("radial", MAX_MULTI_GRAD);

    // Generate LCD text shader
    D3DShaderGen_GenerateLCDTextShader();

    // Genereate Shader to fill Antialiased parallelograms
    D3DShaderGen_GenerateAAParallelogramShader();
#else
    /*
    for (i = 0; i < MAX_RESCALE; i++) {
        D3DShaderGen_GenerateRescaleShader(i);
    }
    D3DShaderGen_WriteShaderArray("rescale", MAX_RESCALE);
    */
    //D3DShaderGen_GenerateConvolveShader(2);
    //D3DShaderGen_GenerateLCDTextShader();
    //D3DShaderGen_GenerateLinearGradShader(16);
    D3DShaderGen_GenerateBasicGradShader(0);
#endif
}

int
main(int argc, char **argv)
{
    fpHeader = fopen(strHeaderFile, "a");

    D3DShaderGen_GenerateAllShaders();

    fclose(fpHeader);

    return 0;
}