3261N/A * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 0N/A * This code is free software; you can redistribute it and/or modify it 0N/A * under the terms of the GNU General Public License version 2 only, as 2362N/A * published by the Free Software Foundation. Oracle designates this 0N/A * particular file as subject to the "Classpath" exception as provided 2362N/A * by Oracle in the LICENSE file that accompanied this code. 0N/A * This code is distributed in the hope that it will be useful, but WITHOUT 0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 0N/A * version 2 for more details (a copy is included in the LICENSE file that 0N/A * accompanied this code). 0N/A * You should have received a copy of the GNU General Public License version 0N/A * 2 along with this work; if not, write to the Free Software Foundation, 0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2362N/A * or visit www.oracle.com if you need additional information or have any 0N/A // assert rq.lock.isHeldByCurrentThread(); 0N/A/****************************** Color support *******************************/ 0N/A // assert rq.lock.isHeldByCurrentThread(); 0N/A/************************* GradientPaint support ****************************/ 0N/A * Note: This code is factored out into a separate static method 0N/A * so that it can be shared by both the Gradient and LinearGradient 0N/A * implementations. LinearGradient uses this code (for the 0N/A * two-color sRGB case only) because it can be much faster than the 0N/A * equivalent implementation that uses fragment shaders. 0N/A * We use OpenGL's texture coordinate generator to automatically 0N/A * apply a smooth gradient (either cyclic or acyclic) to the geometry 0N/A * being rendered. This technique is almost identical to the one 0N/A * described in the comments for BufferedPaints.setTexturePaint(), 0N/A * except the calculations take place in one dimension instead of two. 0N/A * Instead of an anchor rectangle in the TexturePaint case, we use 0N/A * the vector between the two GradientPaint end points in our 0N/A * calculations. The generator uses a single plane equation that 0N/A * takes the (x,y) location (in device space) of the fragment being 0N/A * rendered to calculate a (u) texture coordinate for that fragment: 0N/A * u = Ax + By + Cz + Dw 0N/A * The gradient renderer uses a two-pixel 1D texture where the first 0N/A * pixel contains the first GradientPaint color, and the second pixel 0N/A * contains the second GradientPaint color. (Note that we use the 0N/A * GL_CLAMP_TO_EDGE wrapping mode for acyclic gradients so that we 0N/A * clamp the colors properly at the extremes.) The following diagram 0N/A * attempts to show the layout of the texture containing the two 0N/A * GradientPaint colors (C1 and C2): 0N/A * +-----------------+ 0N/A * +-----------------+ 0N/A * We calculate our plane equation constants (A,B,D) such that u=0.25 0N/A * corresponds to the first GradientPaint end point in user space and 0N/A * u=0.75 corresponds to the second end point. This is somewhat 0N/A * non-obvious, but since the gradient colors are generated by 0N/A * interpolating between C1 and C2, we want the pure color at the 0N/A * end points, and we will get the pure color only when u correlates 0N/A * to the center of a texel. The following chart shows the expected 0N/A * color for some sample values of u (where C' is the color halfway 0N/A * between C1 and C2): 0N/A * u value acyclic (GL_CLAMP) cyclic (GL_REPEAT) 0N/A * ------- ------------------ ------------------ 0N/A * Original inspiration for this technique came from UMD's Agile2D 0N/A // convert gradient colors to IntArgbPre format 0N/A // calculate plane equation constants 0N/A // now gradient point 1 is at the origin 0N/A // now gradient point 2 is on the positive x-axis 0N/A // now gradient point 2 is at (0.5, 0) 0N/A // now gradient point 1 is at (0.25, 0), point 2 is at (0.75, 0) 0N/A // assert rq.lock.isHeldByCurrentThread(); 0N/A/************************** TexturePaint support ****************************/ 0N/A * We use OpenGL's texture coordinate generator to automatically 0N/A * map the TexturePaint image to the geometry being rendered. The 0N/A * generator uses two separate plane equations that take the (x,y) 0N/A * location (in device space) of the fragment being rendered to 0N/A * calculate (u,v) texture coordinates for that fragment: 0N/A * u = Ax + By + Cz + Dw 0N/A * v = Ex + Fy + Gz + Hw 0N/A * Since we use a 2D orthographic projection, we can assume that z=0 0N/A * and w=1 for any fragment. So we need to calculate appropriate 0N/A * values for the plane equation constants (A,B,D) and (E,F,H) such 0N/A * that {u,v}=0 for the top-left of the TexturePaint's anchor 0N/A * rectangle and {u,v}=1 for the bottom-right of the anchor rectangle. 0N/A * We can easily make the texture image repeat for {u,v} values 0N/A * outside the range [0,1] by specifying the GL_REPEAT texture wrap 0N/A * Calculating the plane equation constants is surprisingly simple. 0N/A * We can think of it as an inverse matrix operation that takes 0N/A * device space coordinates and transforms them into user space 0N/A * coordinates that correspond to a location relative to the anchor 0N/A * rectangle. First, we translate and scale the current user space 0N/A * transform by applying the anchor rectangle bounds. We then take 0N/A * the inverse of this affine transform. The rows of the resulting 0N/A * inverse matrix correlate nicely to the plane equation constants 0N/A // calculate plane equation constants 0N/A // assert rq.lock.isHeldByCurrentThread(); 0N/A/****************** Shared MultipleGradientPaint support ********************/ 0N/A * The maximum number of gradient "stops" supported by our native 0N/A * fragment shader implementations. 0N/A * This value has been empirically determined and capped to allow 0N/A * our native shaders to run on all shader-level graphics hardware, 0N/A * even on the older, more limited GPUs. Even the oldest Nvidia 0N/A * hardware could handle 16, or even 32 fractions without any problem. 0N/A * But the first-generation boards from ATI would fall back into 0N/A * software mode (which is unusably slow) for values larger than 12; 0N/A * it appears that those boards do not have enough native registers 0N/A * to support the number of array accesses required by our gradient 0N/A * shaders. So for now we will cap this value at 12, but we can 0N/A * re-evaluate this in the future as hardware becomes more capable. 0N/A * Helper function to convert a color component in sRGB space to 0N/A * linear RGB space. Copied directly from the 0N/A * MultipleGradientPaintContext class. 0N/A * Helper function to convert a (non-premultiplied) Color in sRGB 0N/A * space to an IntArgbPre pixel value, optionally in linear RGB space. 0N/A * Based on the PixelConverter.ArgbPre.rgbToPixel() method. 0N/A return ((a <<
24) | (r <<
16) | (g <<
8) | (b));
0N/A * Converts the given array of Color objects into an int array 0N/A * containing IntArgbPre pixel values. If the linear parameter 0N/A * is true, the Color values will be converted into a linear RGB 0N/A * color space before being returned. 0N/A/********************** LinearGradientPaint support *************************/ 0N/A * This method uses techniques that are nearly identical to those 0N/A * employed in setGradientPaint() above. The primary difference 0N/A * is that at the native level we use a fragment shader to manually 0N/A * apply the plane equation constants to the current fragment position 0N/A * to calculate the gradient position in the range [0,1] (the native 0N/A * code for GradientPaint does the same, except that it uses OpenGL's 0N/A * automatic texture coordinate generation facilities). 0N/A * One other minor difference worth mentioning is that 0N/A * setGradientPaint() calculates the plane equation constants 0N/A * such that the gradient end points are positioned at 0.25 and 0.75 0N/A * (for reasons discussed in the comments for that method). In 0N/A * contrast, for LinearGradientPaint we setup the equation constants 0N/A * such that the gradient end points fall at 0.0 and 1.0. The 0N/A * reason for this difference is that in the fragment shader we 0N/A * have more control over how the gradient values are interpreted 0N/A * (depending on the paint's CycleMethod). 0N/A // delegate to the optimized two-color gradient codepath 0N/A // calculate plane equation constants 0N/A // now gradient point 1 is at the origin 0N/A // now gradient point 2 is on the positive x-axis 0N/A // now gradient point 1 is at (0.0, 0), point 2 is at (1.0, 0) 0N/A // assert rq.lock.isHeldByCurrentThread(); 0N/A/********************** RadialGradientPaint support *************************/ 0N/A * This method calculates six m** values and a focusX value that 0N/A * are used by the native fragment shader. These techniques are 0N/A * based on a whitepaper by Daniel Rice on radial gradient performance 0N/A * (attached to the bug report for 6521533). One can refer to that 0N/A * document for the complete set of formulas and calculations, but 0N/A * the basic goal is to compose a transform that will convert an 0N/A * (x,y) position in device space into a "u" value that represents 0N/A * the relative distance to the gradient focus point. The resulting 0N/A * value can be used to look up the appropriate color by linearly 0N/A * interpolating between the two nearest colors in the gradient. 0N/A // save original (untransformed) center and focus points 0N/A // transform from gradient coords to device coords 0N/A // transform unit circle to gradient coords; we start with the 0N/A // unit circle (center=(0,0), focus on positive x-axis, radius=1) 0N/A // and then transform into gradient space 0N/A // invert to get mapping from device coords to unit circle 0N/A // clamp the focus point so that it does not rest on, or outside 0N/A // of, the circumference of the gradient circle 0N/A // assert rq.lock.isHeldByCurrentThread();