alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (OGLPaints.c)

This example Java source code file (OGLPaints.c) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

gl_bgra, gl_linear, gl_modulate, gl_unsigned_int_8_8_8_8_rev, glfloat, glhandlearb, glint, is_set, j2dtraceln, j2dtraceln1, max_fractions_small, oglpaints_createmultigradprogram, oglpaints_resetpaint, return_if_null

The OGLPaints.c Java example source code

/*
 * 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) {
        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,
            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 */

Other Java examples (source code examples)

Here is a short list of links related to this Java OGLPaints.c source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.