/*
* 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;
}