| /* | |
| * Copyright (c) 2007, 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. | |
| */ | |
| #ifndef HEADLESS | |
| #include <jlong.h> | |
| #include <string.h> | |
| #include "sun_java2d_SunGraphics2D.h" | |
| #include "sun_java2d_pipe_BufferedPaints.h" | |
| #include "OGLPaints.h" | |
| #include "OGLContext.h" | |
| #include "OGLRenderQueue.h" | |
| #include "OGLSurfaceData.h" | |
| void | |
| OGLPaints_ResetPaint(OGLContext *oglc) | |
| { | |
| jubyte ea; | |
| J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_ResetPaint"); | |
| RETURN_IF_NULL(oglc); | |
| J2dTraceLn1(J2D_TRACE_VERBOSE, " state=%d", oglc->paintState); | |
| RESET_PREVIOUS_OP(); | |
| if (oglc->useMask) { | |
| // switch to texture unit 1, where paint state is currently enabled | |
| j2d_glActiveTextureARB(GL_TEXTURE1_ARB); | |
| } | |
| switch (oglc->paintState) { | |
| case sun_java2d_SunGraphics2D_PAINT_GRADIENT: | |
| j2d_glDisable(GL_TEXTURE_1D); | |
| j2d_glDisable(GL_TEXTURE_GEN_S); | |
| break; | |
| case sun_java2d_SunGraphics2D_PAINT_TEXTURE: | |
| // Note: The texture object used in SetTexturePaint() will | |
| // still be bound at this point, so it is safe to call the following. | |
| OGLSD_RESET_TEXTURE_WRAP(GL_TEXTURE_2D); | |
| j2d_glDisable(GL_TEXTURE_2D); | |
| j2d_glDisable(GL_TEXTURE_GEN_S); | |
| j2d_glDisable(GL_TEXTURE_GEN_T); | |
| break; | |
| case sun_java2d_SunGraphics2D_PAINT_LIN_GRADIENT: | |
| case sun_java2d_SunGraphics2D_PAINT_RAD_GRADIENT: | |
| j2d_glUseProgramObjectARB(0); | |
| j2d_glDisable(GL_TEXTURE_1D); | |
| break; | |
| case sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR: | |
| default: | |
| break; | |
| } | |
| if (oglc->useMask) { | |
| // restore control to texture unit 0 | |
| j2d_glActiveTextureARB(GL_TEXTURE0_ARB); | |
| } | |
| // set each component of the current color state to the extra alpha | |
| // value, which will effectively apply the extra alpha to each fragment | |
| // in paint/texturing operations | |
| ea = (jubyte)(oglc->extraAlpha * 0xff + 0.5f); | |
| j2d_glColor4ub(ea, ea, ea, ea); | |
| oglc->pixel = (ea << 24) | (ea << 16) | (ea << 8) | (ea << 0); | |
| oglc->r = ea; | |
| oglc->g = ea; | |
| oglc->b = ea; | |
| oglc->a = ea; | |
| oglc->useMask = JNI_FALSE; | |
| oglc->paintState = -1; | |
| } | |
| void | |
| OGLPaints_SetColor(OGLContext *oglc, jint pixel) | |
| { | |
| jubyte r, g, b, a; | |
| J2dTraceLn1(J2D_TRACE_INFO, "OGLPaints_SetColor: pixel=%08x", pixel); | |
| RETURN_IF_NULL(oglc); | |
| // glColor*() is allowed within glBegin()/glEnd() pairs, so | |
| // no need to reset the current op state here unless the paint | |
| // state really needs to be changed | |
| if (oglc->paintState > sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) { | |
| OGLPaints_ResetPaint(oglc); | |
| } | |
| // store the raw (unmodified) pixel value, which may be used for | |
| // special operations later | |
| oglc->pixel = pixel; | |
| if (oglc->compState != sun_java2d_SunGraphics2D_COMP_XOR) { | |
| r = (jubyte)(pixel >> 16); | |
| g = (jubyte)(pixel >> 8); | |
| b = (jubyte)(pixel >> 0); | |
| a = (jubyte)(pixel >> 24); | |
| J2dTraceLn4(J2D_TRACE_VERBOSE, | |
| " updating color: r=%02x g=%02x b=%02x a=%02x", | |
| r, g, b, a); | |
| } else { | |
| pixel ^= oglc->xorPixel; | |
| r = (jubyte)(pixel >> 16); | |
| g = (jubyte)(pixel >> 8); | |
| b = (jubyte)(pixel >> 0); | |
| a = 0xff; | |
| J2dTraceLn4(J2D_TRACE_VERBOSE, | |
| " updating xor color: r=%02x g=%02x b=%02x xorpixel=%08x", | |
| r, g, b, oglc->xorPixel); | |
| } | |
| j2d_glColor4ub(r, g, b, a); | |
| oglc->r = r; | |
| oglc->g = g; | |
| oglc->b = b; | |
| oglc->a = a; | |
| oglc->useMask = JNI_FALSE; | |
| oglc->paintState = sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR; | |
| } | |
| /************************* GradientPaint support ****************************/ | |
| static GLuint gradientTexID = 0; | |
| static void | |
| OGLPaints_InitGradientTexture() | |
| { | |
| GLclampf priority = 1.0f; | |
| J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_InitGradientTexture"); | |
| j2d_glGenTextures(1, &gradientTexID); | |
| j2d_glBindTexture(GL_TEXTURE_1D, gradientTexID); | |
| j2d_glPrioritizeTextures(1, &gradientTexID, &priority); | |
| j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| j2d_glTexImage1D(GL_TEXTURE_1D, 0, | |
| GL_RGBA8, 2, 0, | |
| GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); | |
| } | |
| void | |
| OGLPaints_SetGradientPaint(OGLContext *oglc, | |
| jboolean useMask, jboolean cyclic, | |
| jdouble p0, jdouble p1, jdouble p3, | |
| jint pixel1, jint pixel2) | |
| { | |
| GLdouble texParams[4]; | |
| GLuint pixels[2]; | |
| J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_SetGradientPaint"); | |
| RETURN_IF_NULL(oglc); | |
| OGLPaints_ResetPaint(oglc); | |
| texParams[0] = p0; | |
| texParams[1] = p1; | |
| texParams[2] = 0.0; | |
| texParams[3] = p3; | |
| pixels[0] = pixel1; | |
| pixels[1] = pixel2; | |
| if (useMask) { | |
| // set up the paint on texture unit 1 (instead of the usual unit 0) | |
| j2d_glActiveTextureARB(GL_TEXTURE1_ARB); | |
| j2d_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); | |
| } else { | |
| // texture unit 0 is already active; we can use the helper macro here | |
| OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE); | |
| } | |
| if (gradientTexID == 0) { | |
| OGLPaints_InitGradientTexture(); | |
| } | |
| j2d_glEnable(GL_TEXTURE_1D); | |
| j2d_glEnable(GL_TEXTURE_GEN_S); | |
| j2d_glBindTexture(GL_TEXTURE_1D, gradientTexID); | |
| j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, | |
| cyclic ? GL_REPEAT : GL_CLAMP_TO_EDGE); | |
| j2d_glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); | |
| j2d_glTexGendv(GL_S, GL_OBJECT_PLANE, texParams); | |
| j2d_glTexSubImage1D(GL_TEXTURE_1D, 0, | |
| 0, 2, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels); | |
| if (useMask) { | |
| // restore control to texture unit 0 | |
| j2d_glActiveTextureARB(GL_TEXTURE0_ARB); | |
| } | |
| // oglc->pixel has been set appropriately in OGLPaints_ResetPaint() | |
| oglc->useMask = useMask; | |
| oglc->paintState = sun_java2d_SunGraphics2D_PAINT_GRADIENT; | |
| } | |
| /************************** TexturePaint support ****************************/ | |
| void | |
| OGLPaints_SetTexturePaint(OGLContext *oglc, | |
| jboolean useMask, | |
| jlong pSrcOps, jboolean filter, | |
| jdouble xp0, jdouble xp1, jdouble xp3, | |
| jdouble yp0, jdouble yp1, jdouble yp3) | |
| { | |
| OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps); | |
| GLdouble xParams[4]; | |
| GLdouble yParams[4]; | |
| GLint hint = (filter ? GL_LINEAR : GL_NEAREST); | |
| J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_SetTexturePaint"); | |
| RETURN_IF_NULL(srcOps); | |
| RETURN_IF_NULL(oglc); | |
| OGLPaints_ResetPaint(oglc); | |
| xParams[0] = xp0; | |
| xParams[1] = xp1; | |
| xParams[2] = 0.0; | |
| xParams[3] = xp3; | |
| yParams[0] = yp0; | |
| yParams[1] = yp1; | |
| yParams[2] = 0.0; | |
| yParams[3] = yp3; | |
| /* | |
| * Note that we explicitly use GL_TEXTURE_2D below rather than using | |
| * srcOps->textureTarget. This is because the texture wrap mode employed | |
| * here (GL_REPEAT) is not available for GL_TEXTURE_RECTANGLE_ARB targets. | |
| * The setup code in OGLPaints.Texture.isPaintValid() and in | |
| * OGLSurfaceData.initTexture() ensures that we only get here for | |
| * GL_TEXTURE_2D targets. | |
| */ | |
| if (useMask) { | |
| // set up the paint on texture unit 1 (instead of the usual unit 0) | |
| j2d_glActiveTextureARB(GL_TEXTURE1_ARB); | |
| j2d_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); | |
| } else { | |
| // texture unit 0 is already active; we can use the helper macro here | |
| OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE); | |
| } | |
| j2d_glEnable(GL_TEXTURE_2D); | |
| j2d_glEnable(GL_TEXTURE_GEN_S); | |
| j2d_glEnable(GL_TEXTURE_GEN_T); | |
| j2d_glBindTexture(GL_TEXTURE_2D, srcOps->textureID); | |
| OGLSD_UPDATE_TEXTURE_FILTER(srcOps, hint); | |
| OGLSD_UPDATE_TEXTURE_WRAP(GL_TEXTURE_2D, GL_REPEAT); | |
| j2d_glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); | |
| j2d_glTexGendv(GL_S, GL_OBJECT_PLANE, xParams); | |
| j2d_glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); | |
| j2d_glTexGendv(GL_T, GL_OBJECT_PLANE, yParams); | |
| if (useMask) { | |
| // restore control to texture unit 0 | |
| j2d_glActiveTextureARB(GL_TEXTURE0_ARB); | |
| } | |
| // oglc->pixel has been set appropriately in OGLPaints_ResetPaint() | |
| oglc->useMask = useMask; | |
| oglc->paintState = sun_java2d_SunGraphics2D_PAINT_TEXTURE; | |
| } | |
| /****************** 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_CYCLE_METHOD | |
| * Placeholder for the CycleMethod enum constant. | |
| * | |
| * MULTI_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_USE_MASK | |
| * If set, apply the alpha mask value from texture unit 0 to the | |
| * final color result (only used in the MaskFill case). | |
| * | |
| * MULTI_LINEAR_RGB | |
| * If set, convert the linear RGB result back into the sRGB color space. | |
| */ | |
| #define MULTI_CYCLE_METHOD (3 << 0) | |
| #define MULTI_LARGE (1 << 2) | |
| #define MULTI_USE_MASK (1 << 3) | |
| #define MULTI_LINEAR_RGB (1 << 4) | |
| /** | |
| * This value determines the size of the array of programs for each | |
| * MultipleGradientPaint type. This value reflects the maximum value that | |
| * can be represented by performing a bitwise-or of all the MULTI_* | |
| * constants defined above. | |
| */ | |
| #define MAX_PROGRAMS 32 | |
| /** Evaluates to true if the given bit is set on the local flags variable. */ | |
| #define IS_SET(flagbit) \ | |
| (((flags) & (flagbit)) != 0) | |
| /** Composes the given parameters as flags into the given flags variable.*/ | |
| #define COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear) \ | |
| do { \ | |
| flags |= ((cycleMethod) & MULTI_CYCLE_METHOD); \ | |
| if (large) flags |= MULTI_LARGE; \ | |
| if (useMask) flags |= MULTI_USE_MASK; \ | |
| if (linear) flags |= MULTI_LINEAR_RGB; \ | |
| } while (0) | |
| /** Extracts the CycleMethod enum value from the given flags variable. */ | |
| #define EXTRACT_CYCLE_METHOD(flags) \ | |
| ((flags) & MULTI_CYCLE_METHOD) | |
| /** | |
| * The maximum number of gradient "stops" supported by the fragment shader | |
| * and related code. When the MULTI_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 sun_java2d_pipe_BufferedPaints_MULTI_MAX_FRACTIONS | |
| #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 | |
| /** | |
| * The handle to the gradient color table texture object used by the shaders. | |
| */ | |
| static GLuint multiGradientTexID = 0; | |
| /** | |
| * This is essentially a template of the shader source code that can be used | |
| * for either LinearGradientPaint or RadialGradientPaint. It includes the | |
| * structure and some variables that are common to each; the remaining | |
| * code snippets (for CycleMethod, ColorSpaceType, and mask modulation) | |
| * are filled in prior to compiling the shader at runtime depending on the | |
| * paint parameters. See OGLPaints_CreateMultiGradProgram() for more details. | |
| */ | |
| static const char *multiGradientShaderSource = | |
| // gradient texture size (in texels) | |
| "const int TEXTURE_SIZE = %d;" | |
| // maximum number of fractions/colors supported by this shader | |
| "const int MAX_FRACTIONS = %d;" | |
| // size of a single texel | |
| "const float FULL_TEXEL = (1.0 / float(TEXTURE_SIZE));" | |
| // size of half of a single texel | |
| "const float HALF_TEXEL = (FULL_TEXEL / 2.0);" | |
| // texture containing the gradient colors | |
| "uniform sampler1D colors;" | |
| // array of gradient stops/fractions | |
| "uniform float fractions[MAX_FRACTIONS];" | |
| // array of scale factors (one for each interval) | |
| "uniform float scaleFactors[MAX_FRACTIONS-1];" | |
| // (placeholder for mask variable) | |
| "%s" | |
| // (placeholder for Linear/RadialGP-specific variables) | |
| "%s" | |
| "" | |
| "void main(void)" | |
| "{" | |
| " float dist;" | |
| // (placeholder for Linear/RadialGradientPaint-specific code) | |
| " %s" | |
| "" | |
| " float tc;" | |
| // (placeholder for CycleMethod-specific code) | |
| " %s" | |
| "" | |
| // calculate interpolated color | |
| " vec4 result = texture1D(colors, tc);" | |
| "" | |
| // (placeholder for ColorSpace conversion code) | |
| " %s" | |
| "" | |
| // (placeholder for mask modulation code) | |
| " %s" | |
| "" | |
| // modulate with gl_Color in order to apply extra alpha | |
| " gl_FragColor = result * gl_Color;" | |
| "}"; | |
| /** | |
| * This code takes a "dist" value as input (as calculated earlier by the | |
| * LGP/RGP-specific code) in the range [0,1] and produces a texture | |
| * coordinate value "tc" that represents the position of the chosen color | |
| * in the one-dimensional gradient texture (also in the range [0,1]). | |
| * | |
| * One naive way to implement this would be to iterate through the fractions | |
| * to figure out in which interval "dist" falls, and then compute the | |
| * relative distance between the two nearest stops. This approach would | |
| * require an "if" check on every iteration, and it is best to avoid | |
| * conditionals in fragment shaders for performance reasons. Also, one might | |
| * be tempted to use a break statement to jump out of the loop once the | |
| * interval was found, but break statements (and non-constant loop bounds) | |
| * are not natively available on most graphics hardware today, so that is | |
| * a non-starter. | |
| * | |
| * The more optimal approach used here avoids these issues entirely by using | |
| * an accumulation function that is equivalent to the process described above. | |
| * The scaleFactors array is pre-initialized at enable time as follows: | |
| * scaleFactors[i] = 1.0 / (fractions[i+1] - fractions[i]); | |
| * | |
| * For each iteration, we subtract fractions[i] from dist and then multiply | |
| * that value by scaleFactors[i]. If we are within the target interval, | |
| * this value will be a fraction in the range [0,1] indicating the relative | |
| * distance between fraction[i] and fraction[i+1]. If we are below the | |
| * target interval, this value will be negative, so we clamp it to zero | |
| * to avoid accumulating any value. If we are above the target interval, | |
| * the value will be greater than one, so we clamp it to one. Upon exiting | |
| * the loop, we will have accumulated zero or more 1.0's and a single | |
| * fractional value. This accumulated value tells us the position of the | |
| * fragment color in the one-dimensional gradient texture, i.e., the | |
| * texcoord called "tc". | |
| */ | |
| static const char *texCoordCalcCode = | |
| "int i;" | |
| "float relFraction = 0.0;" | |
| "for (i = 0; i < MAX_FRACTIONS-1; i++) {" | |
| " relFraction +=" | |
| " clamp((dist - fractions[i]) * scaleFactors[i], 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 | |
| "tc = HALF_TEXEL + (FULL_TEXEL * relFraction);"; | |
| /** Code for NO_CYCLE that gets plugged into the CycleMethod placeholder. */ | |
| static const char *noCycleCode = | |
| "if (dist <= 0.0) {" | |
| " tc = 0.0;" | |
| "} else if (dist >= 1.0) {" | |
| " tc = 1.0;" | |
| "} else {" | |
| // (placeholder for texcoord calculation) | |
| " %s" | |
| "}"; | |
| /** Code for REFLECT that gets plugged into the CycleMethod placeholder. */ | |
| static const char *reflectCode = | |
| "dist = 1.0 - (abs(fract(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 = fract(dist);" | |
| // (placeholder for texcoord calculation) | |
| "%s"; | |
| static void | |
| OGLPaints_InitMultiGradientTexture() | |
| { | |
| GLclampf priority = 1.0f; | |
| J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_InitMultiGradientTexture"); | |
| j2d_glGenTextures(1, &multiGradientTexID); | |
| j2d_glBindTexture(GL_TEXTURE_1D, multiGradientTexID); | |
| j2d_glPrioritizeTextures(1, &multiGradientTexID, &priority); | |
| j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| j2d_glTexImage1D(GL_TEXTURE_1D, 0, | |
| GL_RGBA8, MAX_COLORS, 0, | |
| GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); | |
| } | |
| /** | |
| * Compiles and links the MultipleGradientPaint shader program. If | |
| * successful, this function returns a handle to the newly created | |
| * shader program; otherwise returns 0. | |
| */ | |
| static GLhandleARB | |
| OGLPaints_CreateMultiGradProgram(jint flags, | |
| char *paintVars, char *distCode) | |
| { | |
| GLhandleARB multiGradProgram; | |
| GLint loc; | |
| char *maskVars = ""; | |
| char *maskCode = ""; | |
| char *colorSpaceCode = ""; | |
| char cycleCode[1500]; | |
| char finalSource[3000]; | |
| jint cycleMethod = EXTRACT_CYCLE_METHOD(flags); | |
| jint maxFractions = IS_SET(MULTI_LARGE) ? | |
| MAX_FRACTIONS_LARGE : MAX_FRACTIONS_SMALL; | |
| J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_CreateMultiGradProgram"); | |
| if (IS_SET(MULTI_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 = "uniform sampler2D mask;"; | |
| maskCode = "result *= texture2D(mask, gl_TexCoord[0].st);"; | |
| } else { | |
| /* | |
| * REMIND: This is really wacky, but the gradient shaders will | |
| * produce completely incorrect results on ATI hardware (at least | |
| * on first-gen (R300-based) boards) if the shader program does not | |
| * try to access texture coordinates by using a gl_TexCoord[*] | |
| * variable. This problem really should be addressed by ATI, but | |
| * in the meantime it seems we can workaround the issue by inserting | |
| * a benign operation that accesses gl_TexCoord[0]. Note that we | |
| * only need to do this for ATI boards and only in the !useMask case, | |
| * because the useMask case already does access gl_TexCoord[1] and | |
| * is therefore not affected by this driver bug. | |
| */ | |
| const char *vendor = (const char *)j2d_glGetString(GL_VENDOR); | |
| if (vendor != NULL && strncmp(vendor, "ATI", 3) == 0) { | |
| maskCode = "dist = gl_TexCoord[0].s;"; | |
| } | |
| } | |
| if (IS_SET(MULTI_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, vec3(0.416667)) - 0.055;"; | |
| } | |
| if (cycleMethod == CYCLE_NONE) { | |
| snprintf(cycleCode, sizeof(cycleCode), noCycleCode, texCoordCalcCode); | |
| } else if (cycleMethod == CYCLE_REFLECT) { | |
| snprintf(cycleCode, sizeof(cycleCode), reflectCode, texCoordCalcCode); | |
| } else { // (cycleMethod == CYCLE_REPEAT) | |
| snprintf(cycleCode, sizeof(cycleCode), repeatCode, texCoordCalcCode); | |
| } | |
| // compose the final source code string from the various pieces | |
| snprintf(finalSource, sizeof(finalSource), multiGradientShaderSource, | |
| MAX_COLORS, maxFractions, | |
| maskVars, paintVars, distCode, | |
| cycleCode, colorSpaceCode, maskCode); | |
| multiGradProgram = OGLContext_CreateFragmentProgram(finalSource); | |
| if (multiGradProgram == 0) { | |
| J2dRlsTraceLn(J2D_TRACE_ERROR, | |
| "OGLPaints_CreateMultiGradProgram: error creating program"); | |
| return 0; | |
| } | |
| // "use" the program object temporarily so that we can set the uniforms | |
| j2d_glUseProgramObjectARB(multiGradProgram); | |
| // set the "uniform" texture unit bindings | |
| if (IS_SET(MULTI_USE_MASK)) { | |
| loc = j2d_glGetUniformLocationARB(multiGradProgram, "mask"); | |
| j2d_glUniform1iARB(loc, 0); // texture unit 0 | |
| loc = j2d_glGetUniformLocationARB(multiGradProgram, "colors"); | |
| j2d_glUniform1iARB(loc, 1); // texture unit 1 | |
| } else { | |
| loc = j2d_glGetUniformLocationARB(multiGradProgram, "colors"); | |
| j2d_glUniform1iARB(loc, 0); // texture unit 0 | |
| } | |
| // "unuse" the program object; it will be re-bound later as needed | |
| j2d_glUseProgramObjectARB(0); | |
| if (multiGradientTexID == 0) { | |
| OGLPaints_InitMultiGradientTexture(); | |
| } | |
| return multiGradProgram; | |
| } | |
| /** | |
| * Called from the OGLPaints_SetLinear/RadialGradientPaint() methods | |
| * in order to setup the fraction/color values that are common to both. | |
| */ | |
| static void | |
| OGLPaints_SetMultiGradientPaint(GLhandleARB multiGradProgram, | |
| jint numStops, | |
| void *pFractions, void *pPixels) | |
| { | |
| jint maxFractions = (numStops > MAX_FRACTIONS_SMALL) ? | |
| MAX_FRACTIONS_LARGE : MAX_FRACTIONS_SMALL; | |
| GLfloat scaleFactors[MAX_FRACTIONS-1]; | |
| GLfloat *fractions = (GLfloat *)pFractions; | |
| GLint *pixels = (GLint *)pPixels; | |
| GLint loc; | |
| int i; | |
| // enable the MultipleGradientPaint shader | |
| j2d_glUseProgramObjectARB(multiGradProgram); | |
| // update the "uniform" fraction values | |
| loc = j2d_glGetUniformLocationARB(multiGradProgram, "fractions"); | |
| if (numStops < maxFractions) { | |
| // fill the remainder of the fractions array with all zeros to | |
| // prevent using garbage values from previous paints | |
| GLfloat allZeros[MAX_FRACTIONS]; | |
| memset(allZeros, 0, sizeof(GLfloat)*MAX_FRACTIONS); | |
| j2d_glUniform1fvARB(loc, maxFractions, allZeros); | |
| } | |
| j2d_glUniform1fvARB(loc, numStops, fractions); | |
| // update the "uniform" scale values | |
| loc = j2d_glGetUniformLocationARB(multiGradProgram, "scaleFactors"); | |
| for (i = 0; i < numStops-1; i++) { | |
| // calculate a scale factor for each interval | |
| scaleFactors[i] = 1.0f / (fractions[i+1] - fractions[i]); | |
| } | |
| for (; i < maxFractions-1; i++) { | |
| // fill remaining scale factors with zero | |
| scaleFactors[i] = 0.0f; | |
| } | |
| j2d_glUniform1fvARB(loc, maxFractions-1, scaleFactors); | |
| // update the texture containing the gradient colors | |
| j2d_glEnable(GL_TEXTURE_1D); | |
| j2d_glBindTexture(GL_TEXTURE_1D, multiGradientTexID); | |
| j2d_glTexSubImage1D(GL_TEXTURE_1D, 0, | |
| 0, numStops, | |
| GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, | |
| pixels); | |
| if (numStops < MAX_COLORS) { | |
| // when we don't have enough colors to fill the entire color gradient, | |
| // we have to replicate the last color in the right-most texel for | |
| // the NO_CYCLE case where the texcoord is sometimes forced to 1.0 | |
| j2d_glTexSubImage1D(GL_TEXTURE_1D, 0, | |
| MAX_COLORS-1, 1, | |
| GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, | |
| pixels+(numStops-1)); | |
| } | |
| } | |
| /********************** LinearGradientPaint support *************************/ | |
| /** | |
| * The handles to the LinearGradientPaint fragment program objects. The | |
| * index to the array should be a bitwise-or'ing of the MULTI_* flags defined | |
| * above. Note that most applications will likely need to initialize one | |
| * or two of these elements, so the array is usually sparsely populated. | |
| */ | |
| static GLhandleARB linearGradPrograms[MAX_PROGRAMS]; | |
| /** | |
| * Compiles and links the LinearGradientPaint shader program. If successful, | |
| * this function returns a handle to the newly created shader program; | |
| * otherwise returns 0. | |
| */ | |
| static GLhandleARB | |
| OGLPaints_CreateLinearGradProgram(jint flags) | |
| { | |
| char *paintVars; | |
| char *distCode; | |
| J2dTraceLn1(J2D_TRACE_INFO, | |
| "OGLPaints_CreateLinearGradProgram", | |
| 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 vec3 values below. Here's how the values are related: | |
| * | |
| * params.x = p0 | |
| * params.y = p1 | |
| * params.z = p3 | |
| * | |
| * yoff = dstOps->yOffset + dstOps->height | |
| */ | |
| paintVars = | |
| "uniform vec3 params;" | |
| "uniform float yoff;"; | |
| distCode = | |
| // note that gl_FragCoord is in window space relative to the | |
| // lower-left corner, so we have to flip the y-coordinate here | |
| "vec3 fragCoord = vec3(gl_FragCoord.x, yoff-gl_FragCoord.y, 1.0);" | |
| "dist = dot(params, fragCoord);"; | |
| return OGLPaints_CreateMultiGradProgram(flags, paintVars, distCode); | |
| } | |
| void | |
| OGLPaints_SetLinearGradientPaint(OGLContext *oglc, OGLSDOps *dstOps, | |
| jboolean useMask, jboolean linear, | |
| jint cycleMethod, jint numStops, | |
| jfloat p0, jfloat p1, jfloat p3, | |
| void *fractions, void *pixels) | |
| { | |
| GLhandleARB linearGradProgram; | |
| GLint loc; | |
| jboolean large = (numStops > MAX_FRACTIONS_SMALL); | |
| jint flags = 0; | |
| J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_SetLinearGradientPaint"); | |
| RETURN_IF_NULL(oglc); | |
| RETURN_IF_NULL(dstOps); | |
| OGLPaints_ResetPaint(oglc); | |
| COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear); | |
| if (useMask) { | |
| // set up the paint on texture unit 1 (instead of the usual unit 0) | |
| j2d_glActiveTextureARB(GL_TEXTURE1_ARB); | |
| } | |
| // no need to set GL_MODULATE here (it is ignored when shader is enabled) | |
| // locate/initialize the shader program for the given flags | |
| if (linearGradPrograms[flags] == 0) { | |
| linearGradPrograms[flags] = OGLPaints_CreateLinearGradProgram(flags); | |
| if (linearGradPrograms[flags] == 0) { | |
| // shouldn't happen, but just in case... | |
| return; | |
| } | |
| } | |
| linearGradProgram = linearGradPrograms[flags]; | |
| // update the common "uniform" values (fractions and colors) | |
| OGLPaints_SetMultiGradientPaint(linearGradProgram, | |
| numStops, fractions, pixels); | |
| // update the other "uniform" values | |
| loc = j2d_glGetUniformLocationARB(linearGradProgram, "params"); | |
| j2d_glUniform3fARB(loc, p0, p1, p3); | |
| loc = j2d_glGetUniformLocationARB(linearGradProgram, "yoff"); | |
| j2d_glUniform1fARB(loc, (GLfloat)(dstOps->yOffset + dstOps->height)); | |
| if (useMask) { | |
| // restore control to texture unit 0 | |
| j2d_glActiveTextureARB(GL_TEXTURE0_ARB); | |
| } | |
| // oglc->pixel has been set appropriately in OGLPaints_ResetPaint() | |
| oglc->useMask = useMask; | |
| oglc->paintState = sun_java2d_SunGraphics2D_PAINT_LIN_GRADIENT; | |
| } | |
| /********************** RadialGradientPaint support *************************/ | |
| /** | |
| * The handles to the RadialGradientPaint fragment program objects. The | |
| * index to the array should be a bitwise-or'ing of the MULTI_* flags defined | |
| * above. Note that most applications will likely need to initialize one | |
| * or two of these elements, so the array is usually sparsely populated. | |
| */ | |
| static GLhandleARB radialGradPrograms[MAX_PROGRAMS]; | |
| /** | |
| * Compiles and links the RadialGradientPaint shader program. If successful, | |
| * this function returns a handle to the newly created shader program; | |
| * otherwise returns 0. | |
| */ | |
| static GLhandleARB | |
| OGLPaints_CreateRadialGradProgram(jint flags) | |
| { | |
| char *paintVars; | |
| char *distCode; | |
| J2dTraceLn1(J2D_TRACE_INFO, | |
| "OGLPaints_CreateRadialGradProgram", | |
| 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 vec3 and vec4 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 = yoff = dstOps->yOffset + dstOps->height | |
| * precalc.z = 1.0 - (focusX * focusX) | |
| * precalc.w = 1.0 / precalc.z | |
| */ | |
| paintVars = | |
| "uniform vec3 m0;" | |
| "uniform vec3 m1;" | |
| "uniform vec4 precalc;"; | |
| /* | |
| * 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 = | |
| // note that gl_FragCoord is in window space relative to the | |
| // lower-left corner, so we have to flip the y-coordinate here | |
| "vec3 fragCoord =" | |
| " vec3(gl_FragCoord.x, precalc.y - gl_FragCoord.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.z))*precalc.w;"; | |
| return OGLPaints_CreateMultiGradProgram(flags, paintVars, distCode); | |
| } | |
| void | |
| OGLPaints_SetRadialGradientPaint(OGLContext *oglc, OGLSDOps *dstOps, | |
| jboolean useMask, jboolean linear, | |
| jint cycleMethod, jint numStops, | |
| jfloat m00, jfloat m01, jfloat m02, | |
| jfloat m10, jfloat m11, jfloat m12, | |
| jfloat focusX, | |
| void *fractions, void *pixels) | |
| { | |
| GLhandleARB radialGradProgram; | |
| GLint loc; | |
| GLfloat yoff, denom, inv_denom; | |
| jboolean large = (numStops > MAX_FRACTIONS_SMALL); | |
| jint flags = 0; | |
| J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_SetRadialGradientPaint"); | |
| RETURN_IF_NULL(oglc); | |
| RETURN_IF_NULL(dstOps); | |
| OGLPaints_ResetPaint(oglc); | |
| COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear); | |
| if (useMask) { | |
| // set up the paint on texture unit 1 (instead of the usual unit 0) | |
| j2d_glActiveTextureARB(GL_TEXTURE1_ARB); | |
| } | |
| // no need to set GL_MODULATE here (it is ignored when shader is enabled) | |
| // locate/initialize the shader program for the given flags | |
| if (radialGradPrograms[flags] == 0) { | |
| radialGradPrograms[flags] = OGLPaints_CreateRadialGradProgram(flags); | |
| if (radialGradPrograms[flags] == 0) { | |
| // shouldn't happen, but just in case... | |
| return; | |
| } | |
| } | |
| radialGradProgram = radialGradPrograms[flags]; | |
| // update the common "uniform" values (fractions and colors) | |
| OGLPaints_SetMultiGradientPaint(radialGradProgram, | |
| numStops, fractions, pixels); | |
| // update the other "uniform" values | |
| loc = j2d_glGetUniformLocationARB(radialGradProgram, "m0"); | |
| j2d_glUniform3fARB(loc, m00, m01, m02); | |
| loc = j2d_glGetUniformLocationARB(radialGradProgram, "m1"); | |
| j2d_glUniform3fARB(loc, m10, m11, m12); | |
| // pack a few unrelated, precalculated values into a single vec4 | |
| yoff = (GLfloat)(dstOps->yOffset + dstOps->height); | |
| denom = 1.0f - (focusX * focusX); | |
| inv_denom = 1.0f / denom; | |
| loc = j2d_glGetUniformLocationARB(radialGradProgram, "precalc"); | |
| j2d_glUniform4fARB(loc, focusX, yoff, denom, inv_denom); | |
| if (useMask) { | |
| // restore control to texture unit 0 | |
| j2d_glActiveTextureARB(GL_TEXTURE0_ARB); | |
| } | |
| // oglc->pixel has been set appropriately in OGLPaints_ResetPaint() | |
| oglc->useMask = useMask; | |
| oglc->paintState = sun_java2d_SunGraphics2D_PAINT_RAD_GRADIENT; | |
| } | |
| #endif /* !HEADLESS */ |