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 
39 void
OGLPaints_ResetPaint(OGLContext * oglc)40 OGLPaints_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 
100 void
OGLPaints_SetColor(OGLContext * oglc,jint pixel)101 OGLPaints_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 
153 static GLuint gradientTexID = 0;
154 
155 static void
OGLPaints_InitGradientTexture()156 OGLPaints_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 
172 void
OGLPaints_SetGradientPaint(OGLContext * oglc,jboolean useMask,jboolean cyclic,jdouble p0,jdouble p1,jdouble p3,jint pixel1,jint pixel2)173 OGLPaints_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 
230 void
OGLPaints_SetTexturePaint(OGLContext * oglc,jboolean useMask,jlong pSrcOps,jboolean filter,jdouble xp0,jdouble xp1,jdouble xp3,jdouble yp0,jdouble yp1,jdouble yp3)231 OGLPaints_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  */
382 static 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  */
392 static 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  */
468 static 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. */
480 static 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. */
491 static 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. */
497 static const char *repeatCode =
498     "dist = fract(dist);"
499     // (placeholder for texcoord calculation)
500     "%s";
501 
502 static void
OGLPaints_InitMultiGradientTexture()503 OGLPaints_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  */
525 static GLhandleARB
OGLPaints_CreateMultiGradProgram(jint flags,char * paintVars,char * distCode)526 OGLPaints_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  */
629 static void
OGLPaints_SetMultiGradientPaint(GLhandleARB multiGradProgram,jint numStops,void * pFractions,void * pPixels)630 OGLPaints_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  */
694 static 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  */
701 static GLhandleARB
OGLPaints_CreateLinearGradProgram(jint flags)702 OGLPaints_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 
734 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)735 OGLPaints_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  */
798 static 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  */
805 static GLhandleARB
OGLPaints_CreateRadialGradProgram(jint flags)806 OGLPaints_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 
857 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)858 OGLPaints_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