1/*
2 * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#ifndef HEADLESS
27
28#include <jlong.h>
29#include <string.h>
30
31#include "sun_java2d_SunGraphics2D.h"
32#include "sun_java2d_pipe_BufferedPaints.h"
33
34#include "OGLPaints.h"
35#include "OGLContext.h"
36#include "OGLRenderQueue.h"
37#include "OGLSurfaceData.h"
38
39void
40OGLPaints_ResetPaint(OGLContext *oglc)
41{
42    jubyte ea;
43
44    J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_ResetPaint");
45
46    RETURN_IF_NULL(oglc);
47    J2dTraceLn1(J2D_TRACE_VERBOSE, "  state=%d", oglc->paintState);
48    RESET_PREVIOUS_OP();
49
50    if (oglc->useMask) {
51        // switch to texture unit 1, where paint state is currently enabled
52        j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
53    }
54
55    switch (oglc->paintState) {
56    case sun_java2d_SunGraphics2D_PAINT_GRADIENT:
57        j2d_glDisable(GL_TEXTURE_1D);
58        j2d_glDisable(GL_TEXTURE_GEN_S);
59        break;
60
61    case sun_java2d_SunGraphics2D_PAINT_TEXTURE:
62        // Note: The texture object used in SetTexturePaint() will
63        // still be bound at this point, so it is safe to call the following.
64        OGLSD_RESET_TEXTURE_WRAP(GL_TEXTURE_2D);
65        j2d_glDisable(GL_TEXTURE_2D);
66        j2d_glDisable(GL_TEXTURE_GEN_S);
67        j2d_glDisable(GL_TEXTURE_GEN_T);
68        break;
69
70    case sun_java2d_SunGraphics2D_PAINT_LIN_GRADIENT:
71    case sun_java2d_SunGraphics2D_PAINT_RAD_GRADIENT:
72        j2d_glUseProgramObjectARB(0);
73        j2d_glDisable(GL_TEXTURE_1D);
74        break;
75
76    case sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR:
77    default:
78        break;
79    }
80
81    if (oglc->useMask) {
82        // restore control to texture unit 0
83        j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
84    }
85
86    // set each component of the current color state to the extra alpha
87    // value, which will effectively apply the extra alpha to each fragment
88    // in paint/texturing operations
89    ea = (jubyte)(oglc->extraAlpha * 0xff + 0.5f);
90    j2d_glColor4ub(ea, ea, ea, ea);
91    oglc->pixel = (ea << 24) | (ea << 16) | (ea << 8) | (ea << 0);
92    oglc->r = ea;
93    oglc->g = ea;
94    oglc->b = ea;
95    oglc->a = ea;
96    oglc->useMask = JNI_FALSE;
97    oglc->paintState = -1;
98}
99
100void
101OGLPaints_SetColor(OGLContext *oglc, jint pixel)
102{
103    jubyte r, g, b, a;
104
105    J2dTraceLn1(J2D_TRACE_INFO, "OGLPaints_SetColor: pixel=%08x", pixel);
106
107    RETURN_IF_NULL(oglc);
108
109    // glColor*() is allowed within glBegin()/glEnd() pairs, so
110    // no need to reset the current op state here unless the paint
111    // state really needs to be changed
112    if (oglc->paintState > sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
113        OGLPaints_ResetPaint(oglc);
114    }
115
116    // store the raw (unmodified) pixel value, which may be used for
117    // special operations later
118    oglc->pixel = pixel;
119
120    if (oglc->compState != sun_java2d_SunGraphics2D_COMP_XOR) {
121        r = (jubyte)(pixel >> 16);
122        g = (jubyte)(pixel >>  8);
123        b = (jubyte)(pixel >>  0);
124        a = (jubyte)(pixel >> 24);
125
126        J2dTraceLn4(J2D_TRACE_VERBOSE,
127                    "  updating color: r=%02x g=%02x b=%02x a=%02x",
128                    r, g, b, a);
129    } else {
130        pixel ^= oglc->xorPixel;
131
132        r = (jubyte)(pixel >> 16);
133        g = (jubyte)(pixel >>  8);
134        b = (jubyte)(pixel >>  0);
135        a = 0xff;
136
137        J2dTraceLn4(J2D_TRACE_VERBOSE,
138                    "  updating xor color: r=%02x g=%02x b=%02x xorpixel=%08x",
139                    r, g, b, oglc->xorPixel);
140    }
141
142    j2d_glColor4ub(r, g, b, a);
143    oglc->r = r;
144    oglc->g = g;
145    oglc->b = b;
146    oglc->a = a;
147    oglc->useMask = JNI_FALSE;
148    oglc->paintState = sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR;
149}
150
151/************************* GradientPaint support ****************************/
152
153static GLuint gradientTexID = 0;
154
155static void
156OGLPaints_InitGradientTexture()
157{
158    GLclampf priority = 1.0f;
159
160    J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_InitGradientTexture");
161
162    j2d_glGenTextures(1, &gradientTexID);
163    j2d_glBindTexture(GL_TEXTURE_1D, gradientTexID);
164    j2d_glPrioritizeTextures(1, &gradientTexID, &priority);
165    j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
166    j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
167    j2d_glTexImage1D(GL_TEXTURE_1D, 0,
168                     GL_RGBA8, 2, 0,
169                     GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
170}
171
172void
173OGLPaints_SetGradientPaint(OGLContext *oglc,
174                           jboolean useMask, jboolean cyclic,
175                           jdouble p0, jdouble p1, jdouble p3,
176                           jint pixel1, jint pixel2)
177{
178    GLdouble texParams[4];
179    GLuint pixels[2];
180
181    J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_SetGradientPaint");
182
183    RETURN_IF_NULL(oglc);
184    OGLPaints_ResetPaint(oglc);
185
186    texParams[0] = p0;
187    texParams[1] = p1;
188    texParams[2] = 0.0;
189    texParams[3] = p3;
190
191    pixels[0] = pixel1;
192    pixels[1] = pixel2;
193
194    if (useMask) {
195        // set up the paint on texture unit 1 (instead of the usual unit 0)
196        j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
197        j2d_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
198    } else {
199        // texture unit 0 is already active; we can use the helper macro here
200        OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
201    }
202
203    if (gradientTexID == 0) {
204        OGLPaints_InitGradientTexture();
205    }
206
207    j2d_glEnable(GL_TEXTURE_1D);
208    j2d_glEnable(GL_TEXTURE_GEN_S);
209    j2d_glBindTexture(GL_TEXTURE_1D, gradientTexID);
210    j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S,
211                        cyclic ? GL_REPEAT : GL_CLAMP_TO_EDGE);
212    j2d_glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
213    j2d_glTexGendv(GL_S, GL_OBJECT_PLANE, texParams);
214
215    j2d_glTexSubImage1D(GL_TEXTURE_1D, 0,
216                        0, 2, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
217
218    if (useMask) {
219        // restore control to texture unit 0
220        j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
221    }
222
223    // oglc->pixel has been set appropriately in OGLPaints_ResetPaint()
224    oglc->useMask = useMask;
225    oglc->paintState = sun_java2d_SunGraphics2D_PAINT_GRADIENT;
226}
227
228/************************** TexturePaint support ****************************/
229
230void
231OGLPaints_SetTexturePaint(OGLContext *oglc,
232                          jboolean useMask,
233                          jlong pSrcOps, jboolean filter,
234                          jdouble xp0, jdouble xp1, jdouble xp3,
235                          jdouble yp0, jdouble yp1, jdouble yp3)
236{
237    OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);
238    GLdouble xParams[4];
239    GLdouble yParams[4];
240    GLint hint = (filter ? GL_LINEAR : GL_NEAREST);
241
242    J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_SetTexturePaint");
243
244    RETURN_IF_NULL(srcOps);
245    RETURN_IF_NULL(oglc);
246    OGLPaints_ResetPaint(oglc);
247
248    xParams[0] = xp0;
249    xParams[1] = xp1;
250    xParams[2] = 0.0;
251    xParams[3] = xp3;
252
253    yParams[0] = yp0;
254    yParams[1] = yp1;
255    yParams[2] = 0.0;
256    yParams[3] = yp3;
257
258    /*
259     * Note that we explicitly use GL_TEXTURE_2D below rather than using
260     * srcOps->textureTarget.  This is because the texture wrap mode employed
261     * here (GL_REPEAT) is not available for GL_TEXTURE_RECTANGLE_ARB targets.
262     * The setup code in OGLPaints.Texture.isPaintValid() and in
263     * OGLSurfaceData.initTexture() ensures that we only get here for
264     * GL_TEXTURE_2D targets.
265     */
266
267    if (useMask) {
268        // set up the paint on texture unit 1 (instead of the usual unit 0)
269        j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
270        j2d_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
271    } else {
272        // texture unit 0 is already active; we can use the helper macro here
273        OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
274    }
275
276    j2d_glEnable(GL_TEXTURE_2D);
277    j2d_glEnable(GL_TEXTURE_GEN_S);
278    j2d_glEnable(GL_TEXTURE_GEN_T);
279    j2d_glBindTexture(GL_TEXTURE_2D, srcOps->textureID);
280    OGLSD_UPDATE_TEXTURE_FILTER(srcOps, hint);
281    OGLSD_UPDATE_TEXTURE_WRAP(GL_TEXTURE_2D, GL_REPEAT);
282    j2d_glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
283    j2d_glTexGendv(GL_S, GL_OBJECT_PLANE, xParams);
284    j2d_glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
285    j2d_glTexGendv(GL_T, GL_OBJECT_PLANE, yParams);
286
287    if (useMask) {
288        // restore control to texture unit 0
289        j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
290    }
291
292    // oglc->pixel has been set appropriately in OGLPaints_ResetPaint()
293    oglc->useMask = useMask;
294    oglc->paintState = sun_java2d_SunGraphics2D_PAINT_TEXTURE;
295}
296
297/****************** Shared MultipleGradientPaint support ********************/
298
299/**
300 * These constants are identical to those defined in the
301 * MultipleGradientPaint.CycleMethod enum; they are copied here for
302 * convenience (ideally we would pull them directly from the Java level,
303 * but that entails more hassle than it is worth).
304 */
305#define CYCLE_NONE    0
306#define CYCLE_REFLECT 1
307#define CYCLE_REPEAT  2
308
309/**
310 * The following constants are flags that can be bitwise-or'ed together
311 * to control how the MultipleGradientPaint shader source code is generated:
312 *
313 *   MULTI_CYCLE_METHOD
314 *     Placeholder for the CycleMethod enum constant.
315 *
316 *   MULTI_LARGE
317 *     If set, use the (slower) shader that supports a larger number of
318 *     gradient colors; otherwise, use the optimized codepath.  See
319 *     the MAX_FRACTIONS_SMALL/LARGE constants below for more details.
320 *
321 *   MULTI_USE_MASK
322 *     If set, apply the alpha mask value from texture unit 0 to the
323 *     final color result (only used in the MaskFill case).
324 *
325 *   MULTI_LINEAR_RGB
326 *     If set, convert the linear RGB result back into the sRGB color space.
327 */
328#define MULTI_CYCLE_METHOD (3 << 0)
329#define MULTI_LARGE        (1 << 2)
330#define MULTI_USE_MASK     (1 << 3)
331#define MULTI_LINEAR_RGB   (1 << 4)
332
333/**
334 * This value determines the size of the array of programs for each
335 * MultipleGradientPaint type.  This value reflects the maximum value that
336 * can be represented by performing a bitwise-or of all the MULTI_*
337 * constants defined above.
338 */
339#define MAX_PROGRAMS 32
340
341/** Evaluates to true if the given bit is set on the local flags variable. */
342#define IS_SET(flagbit) \
343    (((flags) & (flagbit)) != 0)
344
345/** Composes the given parameters as flags into the given flags variable.*/
346#define COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear) \
347    do {                                                   \
348        flags |= ((cycleMethod) & MULTI_CYCLE_METHOD);     \
349        if (large)   flags |= MULTI_LARGE;                 \
350        if (useMask) flags |= MULTI_USE_MASK;              \
351        if (linear)  flags |= MULTI_LINEAR_RGB;            \
352    } while (0)
353
354/** Extracts the CycleMethod enum value from the given flags variable. */
355#define EXTRACT_CYCLE_METHOD(flags) \
356    ((flags) & MULTI_CYCLE_METHOD)
357
358/**
359 * The maximum number of gradient "stops" supported by the fragment shader
360 * and related code.  When the MULTI_LARGE flag is set, we will use
361 * MAX_FRACTIONS_LARGE; otherwise, we use MAX_FRACTIONS_SMALL.  By having
362 * two separate values, we can have one highly optimized shader (SMALL) that
363 * supports only a few fractions/colors, and then another, less optimal
364 * shader that supports more stops.
365 */
366#define MAX_FRACTIONS sun_java2d_pipe_BufferedPaints_MULTI_MAX_FRACTIONS
367#define MAX_FRACTIONS_LARGE MAX_FRACTIONS
368#define MAX_FRACTIONS_SMALL 4
369
370/**
371 * The maximum number of gradient colors supported by all of the gradient
372 * fragment shaders.  Note that this value must be a power of two, as it
373 * determines the size of the 1D texture created below.  It also must be
374 * greater than or equal to MAX_FRACTIONS (there is no strict requirement
375 * that the two values be equal).
376 */
377#define MAX_COLORS 16
378
379/**
380 * The handle to the gradient color table texture object used by the shaders.
381 */
382static GLuint multiGradientTexID = 0;
383
384/**
385 * This is essentially a template of the shader source code that can be used
386 * for either LinearGradientPaint or RadialGradientPaint.  It includes the
387 * structure and some variables that are common to each; the remaining
388 * code snippets (for CycleMethod, ColorSpaceType, and mask modulation)
389 * are filled in prior to compiling the shader at runtime depending on the
390 * paint parameters.  See OGLPaints_CreateMultiGradProgram() for more details.
391 */
392static const char *multiGradientShaderSource =
393    // gradient texture size (in texels)
394    "const int TEXTURE_SIZE = %d;"
395    // maximum number of fractions/colors supported by this shader
396    "const int MAX_FRACTIONS = %d;"
397    // size of a single texel
398    "const float FULL_TEXEL = (1.0 / float(TEXTURE_SIZE));"
399    // size of half of a single texel
400    "const float HALF_TEXEL = (FULL_TEXEL / 2.0);"
401    // texture containing the gradient colors
402    "uniform sampler1D colors;"
403    // array of gradient stops/fractions
404    "uniform float fractions[MAX_FRACTIONS];"
405    // array of scale factors (one for each interval)
406    "uniform float scaleFactors[MAX_FRACTIONS-1];"
407    // (placeholder for mask variable)
408    "%s"
409    // (placeholder for Linear/RadialGP-specific variables)
410    "%s"
411    ""
412    "void main(void)"
413    "{"
414    "    float dist;"
415         // (placeholder for Linear/RadialGradientPaint-specific code)
416    "    %s"
417    ""
418    "    float tc;"
419         // (placeholder for CycleMethod-specific code)
420    "    %s"
421    ""
422         // calculate interpolated color
423    "    vec4 result = texture1D(colors, tc);"
424    ""
425         // (placeholder for ColorSpace conversion code)
426    "    %s"
427    ""
428         // (placeholder for mask modulation code)
429    "    %s"
430    ""
431         // modulate with gl_Color in order to apply extra alpha
432    "    gl_FragColor = result * gl_Color;"
433    "}";
434
435/**
436 * This code takes a "dist" value as input (as calculated earlier by the
437 * LGP/RGP-specific code) in the range [0,1] and produces a texture
438 * coordinate value "tc" that represents the position of the chosen color
439 * in the one-dimensional gradient texture (also in the range [0,1]).
440 *
441 * One naive way to implement this would be to iterate through the fractions
442 * to figure out in which interval "dist" falls, and then compute the
443 * relative distance between the two nearest stops.  This approach would
444 * require an "if" check on every iteration, and it is best to avoid
445 * conditionals in fragment shaders for performance reasons.  Also, one might
446 * be tempted to use a break statement to jump out of the loop once the
447 * interval was found, but break statements (and non-constant loop bounds)
448 * are not natively available on most graphics hardware today, so that is
449 * a non-starter.
450 *
451 * The more optimal approach used here avoids these issues entirely by using
452 * an accumulation function that is equivalent to the process described above.
453 * The scaleFactors array is pre-initialized at enable time as follows:
454 *     scaleFactors[i] = 1.0 / (fractions[i+1] - fractions[i]);
455 *
456 * For each iteration, we subtract fractions[i] from dist and then multiply
457 * that value by scaleFactors[i].  If we are within the target interval,
458 * this value will be a fraction in the range [0,1] indicating the relative
459 * distance between fraction[i] and fraction[i+1].  If we are below the
460 * target interval, this value will be negative, so we clamp it to zero
461 * to avoid accumulating any value.  If we are above the target interval,
462 * the value will be greater than one, so we clamp it to one.  Upon exiting
463 * the loop, we will have accumulated zero or more 1.0's and a single
464 * fractional value.  This accumulated value tells us the position of the
465 * fragment color in the one-dimensional gradient texture, i.e., the
466 * texcoord called "tc".
467 */
468static const char *texCoordCalcCode =
469    "int i;"
470    "float relFraction = 0.0;"
471    "for (i = 0; i < MAX_FRACTIONS-1; i++) {"
472    "    relFraction +="
473    "        clamp((dist - fractions[i]) * scaleFactors[i], 0.0, 1.0);"
474    "}"
475    // we offset by half a texel so that we find the linearly interpolated
476    // color between the two texel centers of interest
477    "tc = HALF_TEXEL + (FULL_TEXEL * relFraction);";
478
479/** Code for NO_CYCLE that gets plugged into the CycleMethod placeholder. */
480static const char *noCycleCode =
481    "if (dist <= 0.0) {"
482    "    tc = 0.0;"
483    "} else if (dist >= 1.0) {"
484    "    tc = 1.0;"
485    "} else {"
486         // (placeholder for texcoord calculation)
487    "    %s"
488    "}";
489
490/** Code for REFLECT that gets plugged into the CycleMethod placeholder. */
491static const char *reflectCode =
492    "dist = 1.0 - (abs(fract(dist * 0.5) - 0.5) * 2.0);"
493    // (placeholder for texcoord calculation)
494    "%s";
495
496/** Code for REPEAT that gets plugged into the CycleMethod placeholder. */
497static const char *repeatCode =
498    "dist = fract(dist);"
499    // (placeholder for texcoord calculation)
500    "%s";
501
502static void
503OGLPaints_InitMultiGradientTexture()
504{
505    GLclampf priority = 1.0f;
506
507    J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_InitMultiGradientTexture");
508
509    j2d_glGenTextures(1, &multiGradientTexID);
510    j2d_glBindTexture(GL_TEXTURE_1D, multiGradientTexID);
511    j2d_glPrioritizeTextures(1, &multiGradientTexID, &priority);
512    j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
513    j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
514    j2d_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
515    j2d_glTexImage1D(GL_TEXTURE_1D, 0,
516                     GL_RGBA8, MAX_COLORS, 0,
517                     GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
518}
519
520/**
521 * Compiles and links the MultipleGradientPaint shader program.  If
522 * successful, this function returns a handle to the newly created
523 * shader program; otherwise returns 0.
524 */
525static GLhandleARB
526OGLPaints_CreateMultiGradProgram(jint flags,
527                                 char *paintVars, char *distCode)
528{
529    GLhandleARB multiGradProgram;
530    GLint loc;
531    char *maskVars = "";
532    char *maskCode = "";
533    char *colorSpaceCode = "";
534    char cycleCode[1500];
535    char finalSource[3000];
536    jint cycleMethod = EXTRACT_CYCLE_METHOD(flags);
537    jint maxFractions = IS_SET(MULTI_LARGE) ?
538        MAX_FRACTIONS_LARGE : MAX_FRACTIONS_SMALL;
539
540    J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_CreateMultiGradProgram");
541
542    if (IS_SET(MULTI_USE_MASK)) {
543        /*
544         * This code modulates the calculated result color with the
545         * corresponding alpha value from the alpha mask texture active
546         * on texture unit 0.  Only needed when useMask is true (i.e., only
547         * for MaskFill operations).
548         */
549        maskVars = "uniform sampler2D mask;";
550        maskCode = "result *= texture2D(mask, gl_TexCoord[0].st);";
551    } else {
552        /*
553         * REMIND: This is really wacky, but the gradient shaders will
554         * produce completely incorrect results on ATI hardware (at least
555         * on first-gen (R300-based) boards) if the shader program does not
556         * try to access texture coordinates by using a gl_TexCoord[*]
557         * variable.  This problem really should be addressed by ATI, but
558         * in the meantime it seems we can workaround the issue by inserting
559         * a benign operation that accesses gl_TexCoord[0].  Note that we
560         * only need to do this for ATI boards and only in the !useMask case,
561         * because the useMask case already does access gl_TexCoord[1] and
562         * is therefore not affected by this driver bug.
563         */
564        const char *vendor = (const char *)j2d_glGetString(GL_VENDOR);
565        if (vendor != NULL && strncmp(vendor, "ATI", 3) == 0) {
566            maskCode = "dist = gl_TexCoord[0].s;";
567        }
568    }
569
570    if (IS_SET(MULTI_LINEAR_RGB)) {
571        /*
572         * This code converts a single pixel in linear RGB space back
573         * into sRGB (note: this code was adapted from the
574         * MultipleGradientPaintContext.convertLinearRGBtoSRGB() method).
575         */
576        colorSpaceCode =
577            "result.rgb = 1.055 * pow(result.rgb, vec3(0.416667)) - 0.055;";
578    }
579
580    if (cycleMethod == CYCLE_NONE) {
581        sprintf(cycleCode, noCycleCode, texCoordCalcCode);
582    } else if (cycleMethod == CYCLE_REFLECT) {
583        sprintf(cycleCode, reflectCode, texCoordCalcCode);
584    } else { // (cycleMethod == CYCLE_REPEAT)
585        sprintf(cycleCode, repeatCode, texCoordCalcCode);
586    }
587
588    // compose the final source code string from the various pieces
589    sprintf(finalSource, multiGradientShaderSource,
590            MAX_COLORS, maxFractions,
591            maskVars, paintVars, distCode,
592            cycleCode, colorSpaceCode, maskCode);
593
594    multiGradProgram = OGLContext_CreateFragmentProgram(finalSource);
595    if (multiGradProgram == 0) {
596        J2dRlsTraceLn(J2D_TRACE_ERROR,
597            "OGLPaints_CreateMultiGradProgram: error creating program");
598        return 0;
599    }
600
601    // "use" the program object temporarily so that we can set the uniforms
602    j2d_glUseProgramObjectARB(multiGradProgram);
603
604    // set the "uniform" texture unit bindings
605    if (IS_SET(MULTI_USE_MASK)) {
606        loc = j2d_glGetUniformLocationARB(multiGradProgram, "mask");
607        j2d_glUniform1iARB(loc, 0); // texture unit 0
608        loc = j2d_glGetUniformLocationARB(multiGradProgram, "colors");
609        j2d_glUniform1iARB(loc, 1); // texture unit 1
610    } else {
611        loc = j2d_glGetUniformLocationARB(multiGradProgram, "colors");
612        j2d_glUniform1iARB(loc, 0); // texture unit 0
613    }
614
615    // "unuse" the program object; it will be re-bound later as needed
616    j2d_glUseProgramObjectARB(0);
617
618    if (multiGradientTexID == 0) {
619        OGLPaints_InitMultiGradientTexture();
620    }
621
622    return multiGradProgram;
623}
624
625/**
626 * Called from the OGLPaints_SetLinear/RadialGradientPaint() methods
627 * in order to setup the fraction/color values that are common to both.
628 */
629static void
630OGLPaints_SetMultiGradientPaint(GLhandleARB multiGradProgram,
631                                jint numStops,
632                                void *pFractions, void *pPixels)
633{
634    jint maxFractions = (numStops > MAX_FRACTIONS_SMALL) ?
635        MAX_FRACTIONS_LARGE : MAX_FRACTIONS_SMALL;
636    GLfloat scaleFactors[MAX_FRACTIONS-1];
637    GLfloat *fractions = (GLfloat *)pFractions;
638    GLint *pixels = (GLint *)pPixels;
639    GLint loc;
640    int i;
641
642    // enable the MultipleGradientPaint shader
643    j2d_glUseProgramObjectARB(multiGradProgram);
644
645    // update the "uniform" fraction values
646    loc = j2d_glGetUniformLocationARB(multiGradProgram, "fractions");
647    if (numStops < maxFractions) {
648        // fill the remainder of the fractions array with all zeros to
649        // prevent using garbage values from previous paints
650        GLfloat allZeros[MAX_FRACTIONS];
651        memset(allZeros, 0, sizeof(GLfloat)*MAX_FRACTIONS);
652        j2d_glUniform1fvARB(loc, maxFractions, allZeros);
653    }
654    j2d_glUniform1fvARB(loc, numStops, fractions);
655
656    // update the "uniform" scale values
657    loc = j2d_glGetUniformLocationARB(multiGradProgram, "scaleFactors");
658    for (i = 0; i < numStops-1; i++) {
659        // calculate a scale factor for each interval
660        scaleFactors[i] = 1.0f / (fractions[i+1] - fractions[i]);
661    }
662    for (; i < maxFractions-1; i++) {
663        // fill remaining scale factors with zero
664        scaleFactors[i] = 0.0f;
665    }
666    j2d_glUniform1fvARB(loc, maxFractions-1, scaleFactors);
667
668    // update the texture containing the gradient colors
669    j2d_glEnable(GL_TEXTURE_1D);
670    j2d_glBindTexture(GL_TEXTURE_1D, multiGradientTexID);
671    j2d_glTexSubImage1D(GL_TEXTURE_1D, 0,
672                        0, numStops,
673                        GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
674                        pixels);
675    if (numStops < MAX_COLORS) {
676        // when we don't have enough colors to fill the entire color gradient,
677        // we have to replicate the last color in the right-most texel for
678        // the NO_CYCLE case where the texcoord is sometimes forced to 1.0
679        j2d_glTexSubImage1D(GL_TEXTURE_1D, 0,
680                            MAX_COLORS-1, 1,
681                            GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
682                            pixels+(numStops-1));
683    }
684}
685
686/********************** LinearGradientPaint support *************************/
687
688/**
689 * The handles to the LinearGradientPaint fragment program objects.  The
690 * index to the array should be a bitwise-or'ing of the MULTI_* flags defined
691 * above.  Note that most applications will likely need to initialize one
692 * or two of these elements, so the array is usually sparsely populated.
693 */
694static GLhandleARB linearGradPrograms[MAX_PROGRAMS];
695
696/**
697 * Compiles and links the LinearGradientPaint shader program.  If successful,
698 * this function returns a handle to the newly created shader program;
699 * otherwise returns 0.
700 */
701static GLhandleARB
702OGLPaints_CreateLinearGradProgram(jint flags)
703{
704    char *paintVars;
705    char *distCode;
706
707    J2dTraceLn1(J2D_TRACE_INFO,
708                "OGLPaints_CreateLinearGradProgram",
709                flags);
710
711    /*
712     * To simplify the code and to make it easier to upload a number of
713     * uniform values at once, we pack a bunch of scalar (float) values
714     * into vec3 values below.  Here's how the values are related:
715     *
716     *   params.x = p0
717     *   params.y = p1
718     *   params.z = p3
719     *
720     *   yoff = dstOps->yOffset + dstOps->height
721     */
722    paintVars =
723        "uniform vec3 params;"
724        "uniform float yoff;";
725    distCode =
726        // note that gl_FragCoord is in window space relative to the
727        // lower-left corner, so we have to flip the y-coordinate here
728        "vec3 fragCoord = vec3(gl_FragCoord.x, yoff-gl_FragCoord.y, 1.0);"
729        "dist = dot(params, fragCoord);";
730
731    return OGLPaints_CreateMultiGradProgram(flags, paintVars, distCode);
732}
733
734void
735OGLPaints_SetLinearGradientPaint(OGLContext *oglc, OGLSDOps *dstOps,
736                                 jboolean useMask, jboolean linear,
737                                 jint cycleMethod, jint numStops,
738                                 jfloat p0, jfloat p1, jfloat p3,
739                                 void *fractions, void *pixels)
740{
741    GLhandleARB linearGradProgram;
742    GLint loc;
743    jboolean large = (numStops > MAX_FRACTIONS_SMALL);
744    jint flags = 0;
745
746    J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_SetLinearGradientPaint");
747
748    RETURN_IF_NULL(oglc);
749    RETURN_IF_NULL(dstOps);
750    OGLPaints_ResetPaint(oglc);
751
752    COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear);
753
754    if (useMask) {
755        // set up the paint on texture unit 1 (instead of the usual unit 0)
756        j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
757    }
758    // no need to set GL_MODULATE here (it is ignored when shader is enabled)
759
760    // locate/initialize the shader program for the given flags
761    if (linearGradPrograms[flags] == 0) {
762        linearGradPrograms[flags] = OGLPaints_CreateLinearGradProgram(flags);
763        if (linearGradPrograms[flags] == 0) {
764            // shouldn't happen, but just in case...
765            return;
766        }
767    }
768    linearGradProgram = linearGradPrograms[flags];
769
770    // update the common "uniform" values (fractions and colors)
771    OGLPaints_SetMultiGradientPaint(linearGradProgram,
772                                    numStops, fractions, pixels);
773
774    // update the other "uniform" values
775    loc = j2d_glGetUniformLocationARB(linearGradProgram, "params");
776    j2d_glUniform3fARB(loc, p0, p1, p3);
777    loc = j2d_glGetUniformLocationARB(linearGradProgram, "yoff");
778    j2d_glUniform1fARB(loc, (GLfloat)(dstOps->yOffset + dstOps->height));
779
780    if (useMask) {
781        // restore control to texture unit 0
782        j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
783    }
784
785    // oglc->pixel has been set appropriately in OGLPaints_ResetPaint()
786    oglc->useMask = useMask;
787    oglc->paintState = sun_java2d_SunGraphics2D_PAINT_LIN_GRADIENT;
788}
789
790/********************** RadialGradientPaint support *************************/
791
792/**
793 * The handles to the RadialGradientPaint fragment program objects.  The
794 * index to the array should be a bitwise-or'ing of the MULTI_* flags defined
795 * above.  Note that most applications will likely need to initialize one
796 * or two of these elements, so the array is usually sparsely populated.
797 */
798static GLhandleARB radialGradPrograms[MAX_PROGRAMS];
799
800/**
801 * Compiles and links the RadialGradientPaint shader program.  If successful,
802 * this function returns a handle to the newly created shader program;
803 * otherwise returns 0.
804 */
805static GLhandleARB
806OGLPaints_CreateRadialGradProgram(jint flags)
807{
808    char *paintVars;
809    char *distCode;
810
811    J2dTraceLn1(J2D_TRACE_INFO,
812                "OGLPaints_CreateRadialGradProgram",
813                flags);
814
815    /*
816     * To simplify the code and to make it easier to upload a number of
817     * uniform values at once, we pack a bunch of scalar (float) values
818     * into vec3 and vec4 values below.  Here's how the values are related:
819     *
820     *   m0.x = m00
821     *   m0.y = m01
822     *   m0.z = m02
823     *
824     *   m1.x = m10
825     *   m1.y = m11
826     *   m1.z = m12
827     *
828     *   precalc.x = focusX
829     *   precalc.y = yoff = dstOps->yOffset + dstOps->height
830     *   precalc.z = 1.0 - (focusX * focusX)
831     *   precalc.w = 1.0 / precalc.z
832     */
833    paintVars =
834        "uniform vec3 m0;"
835        "uniform vec3 m1;"
836        "uniform vec4 precalc;";
837
838    /*
839     * The following code is derived from Daniel Rice's whitepaper on
840     * radial gradient performance (attached to the bug report for 6521533).
841     * Refer to that document as well as the setup code in the Java-level
842     * BufferedPaints.setRadialGradientPaint() method for more details.
843     */
844    distCode =
845        // note that gl_FragCoord is in window space relative to the
846        // lower-left corner, so we have to flip the y-coordinate here
847        "vec3 fragCoord ="
848        "    vec3(gl_FragCoord.x, precalc.y - gl_FragCoord.y, 1.0);"
849        "float x = dot(fragCoord, m0);"
850        "float y = dot(fragCoord, m1);"
851        "float xfx = x - precalc.x;"
852        "dist = (precalc.x*xfx + sqrt(xfx*xfx + y*y*precalc.z))*precalc.w;";
853
854    return OGLPaints_CreateMultiGradProgram(flags, paintVars, distCode);
855}
856
857void
858OGLPaints_SetRadialGradientPaint(OGLContext *oglc, OGLSDOps *dstOps,
859                                 jboolean useMask, jboolean linear,
860                                 jint cycleMethod, jint numStops,
861                                 jfloat m00, jfloat m01, jfloat m02,
862                                 jfloat m10, jfloat m11, jfloat m12,
863                                 jfloat focusX,
864                                 void *fractions, void *pixels)
865{
866    GLhandleARB radialGradProgram;
867    GLint loc;
868    GLfloat yoff, denom, inv_denom;
869    jboolean large = (numStops > MAX_FRACTIONS_SMALL);
870    jint flags = 0;
871
872    J2dTraceLn(J2D_TRACE_INFO, "OGLPaints_SetRadialGradientPaint");
873
874    RETURN_IF_NULL(oglc);
875    RETURN_IF_NULL(dstOps);
876    OGLPaints_ResetPaint(oglc);
877
878    COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear);
879
880    if (useMask) {
881        // set up the paint on texture unit 1 (instead of the usual unit 0)
882        j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
883    }
884    // no need to set GL_MODULATE here (it is ignored when shader is enabled)
885
886    // locate/initialize the shader program for the given flags
887    if (radialGradPrograms[flags] == 0) {
888        radialGradPrograms[flags] = OGLPaints_CreateRadialGradProgram(flags);
889        if (radialGradPrograms[flags] == 0) {
890            // shouldn't happen, but just in case...
891            return;
892        }
893    }
894    radialGradProgram = radialGradPrograms[flags];
895
896    // update the common "uniform" values (fractions and colors)
897    OGLPaints_SetMultiGradientPaint(radialGradProgram,
898                                    numStops, fractions, pixels);
899
900    // update the other "uniform" values
901    loc = j2d_glGetUniformLocationARB(radialGradProgram, "m0");
902    j2d_glUniform3fARB(loc, m00, m01, m02);
903    loc = j2d_glGetUniformLocationARB(radialGradProgram, "m1");
904    j2d_glUniform3fARB(loc, m10, m11, m12);
905
906    // pack a few unrelated, precalculated values into a single vec4
907    yoff = (GLfloat)(dstOps->yOffset + dstOps->height);
908    denom = 1.0f - (focusX * focusX);
909    inv_denom = 1.0f / denom;
910    loc = j2d_glGetUniformLocationARB(radialGradProgram, "precalc");
911    j2d_glUniform4fARB(loc, focusX, yoff, denom, inv_denom);
912
913    if (useMask) {
914        // restore control to texture unit 0
915        j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
916    }
917
918    // oglc->pixel has been set appropriately in OGLPaints_ResetPaint()
919    oglc->useMask = useMask;
920    oglc->paintState = sun_java2d_SunGraphics2D_PAINT_RAD_GRADIENT;
921}
922
923#endif /* !HEADLESS */
924