author | dxu |
Thu, 04 Apr 2013 15:39:17 -0700 | |
changeset 16734 | da1901d79073 |
parent 12813 | c10ab96dcf41 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
16734
da1901d79073
8000406: change files using @GenerateNativeHeader to use @Native
dxu
parents:
12813
diff
changeset
|
2 |
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 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 |
* |
|
5506 | 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. |
|
2 | 24 |
*/ |
25 |
||
26 |
package sun.java2d.pipe; |
|
27 |
||
28 |
import java.awt.Color; |
|
29 |
import java.awt.GradientPaint; |
|
30 |
import java.awt.LinearGradientPaint; |
|
31 |
import java.awt.MultipleGradientPaint; |
|
32 |
import java.awt.MultipleGradientPaint.ColorSpaceType; |
|
33 |
import java.awt.MultipleGradientPaint.CycleMethod; |
|
34 |
import java.awt.Paint; |
|
35 |
import java.awt.RadialGradientPaint; |
|
36 |
import java.awt.TexturePaint; |
|
37 |
import java.awt.geom.AffineTransform; |
|
38 |
import java.awt.geom.Point2D; |
|
39 |
import java.awt.geom.Rectangle2D; |
|
40 |
import java.awt.image.AffineTransformOp; |
|
41 |
import java.awt.image.BufferedImage; |
|
42 |
import sun.awt.image.PixelConverter; |
|
43 |
import sun.java2d.SunGraphics2D; |
|
44 |
import sun.java2d.SurfaceData; |
|
45 |
import sun.java2d.loops.CompositeType; |
|
46 |
import sun.java2d.loops.SurfaceType; |
|
47 |
import static sun.java2d.pipe.BufferedOpCodes.*; |
|
48 |
||
16734
da1901d79073
8000406: change files using @GenerateNativeHeader to use @Native
dxu
parents:
12813
diff
changeset
|
49 |
import java.lang.annotation.Native; |
12813
c10ab96dcf41
7170969: Add @GenerateNativeHeader to classes whose fields need to be exported for JNI
erikj
parents:
11080
diff
changeset
|
50 |
|
2 | 51 |
public class BufferedPaints { |
52 |
||
53 |
static void setPaint(RenderQueue rq, SunGraphics2D sg2d, |
|
54 |
Paint paint, int ctxflags) |
|
55 |
{ |
|
56 |
if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) { |
|
57 |
setColor(rq, sg2d.pixel); |
|
58 |
} else { |
|
59 |
boolean useMask = (ctxflags & BufferedContext.USE_MASK) != 0; |
|
60 |
switch (sg2d.paintState) { |
|
61 |
case SunGraphics2D.PAINT_GRADIENT: |
|
62 |
setGradientPaint(rq, sg2d, |
|
63 |
(GradientPaint)paint, useMask); |
|
64 |
break; |
|
65 |
case SunGraphics2D.PAINT_LIN_GRADIENT: |
|
66 |
setLinearGradientPaint(rq, sg2d, |
|
67 |
(LinearGradientPaint)paint, useMask); |
|
68 |
break; |
|
69 |
case SunGraphics2D.PAINT_RAD_GRADIENT: |
|
70 |
setRadialGradientPaint(rq, sg2d, |
|
71 |
(RadialGradientPaint)paint, useMask); |
|
72 |
break; |
|
73 |
case SunGraphics2D.PAINT_TEXTURE: |
|
74 |
setTexturePaint(rq, sg2d, |
|
75 |
(TexturePaint)paint, useMask); |
|
76 |
break; |
|
77 |
default: |
|
78 |
break; |
|
79 |
} |
|
80 |
} |
|
81 |
} |
|
82 |
||
83 |
static void resetPaint(RenderQueue rq) { |
|
84 |
// assert rq.lock.isHeldByCurrentThread(); |
|
85 |
rq.ensureCapacity(4); |
|
86 |
RenderBuffer buf = rq.getBuffer(); |
|
87 |
buf.putInt(RESET_PAINT); |
|
88 |
} |
|
89 |
||
90 |
/****************************** Color support *******************************/ |
|
91 |
||
92 |
private static void setColor(RenderQueue rq, int pixel) { |
|
93 |
// assert rq.lock.isHeldByCurrentThread(); |
|
94 |
rq.ensureCapacity(8); |
|
95 |
RenderBuffer buf = rq.getBuffer(); |
|
96 |
buf.putInt(SET_COLOR); |
|
97 |
buf.putInt(pixel); |
|
98 |
} |
|
99 |
||
100 |
/************************* GradientPaint support ****************************/ |
|
101 |
||
102 |
/** |
|
103 |
* Note: This code is factored out into a separate static method |
|
104 |
* so that it can be shared by both the Gradient and LinearGradient |
|
105 |
* implementations. LinearGradient uses this code (for the |
|
106 |
* two-color sRGB case only) because it can be much faster than the |
|
107 |
* equivalent implementation that uses fragment shaders. |
|
108 |
* |
|
109 |
* We use OpenGL's texture coordinate generator to automatically |
|
110 |
* apply a smooth gradient (either cyclic or acyclic) to the geometry |
|
111 |
* being rendered. This technique is almost identical to the one |
|
112 |
* described in the comments for BufferedPaints.setTexturePaint(), |
|
113 |
* except the calculations take place in one dimension instead of two. |
|
114 |
* Instead of an anchor rectangle in the TexturePaint case, we use |
|
115 |
* the vector between the two GradientPaint end points in our |
|
116 |
* calculations. The generator uses a single plane equation that |
|
117 |
* takes the (x,y) location (in device space) of the fragment being |
|
118 |
* rendered to calculate a (u) texture coordinate for that fragment: |
|
119 |
* u = Ax + By + Cz + Dw |
|
120 |
* |
|
121 |
* The gradient renderer uses a two-pixel 1D texture where the first |
|
122 |
* pixel contains the first GradientPaint color, and the second pixel |
|
123 |
* contains the second GradientPaint color. (Note that we use the |
|
124 |
* GL_CLAMP_TO_EDGE wrapping mode for acyclic gradients so that we |
|
125 |
* clamp the colors properly at the extremes.) The following diagram |
|
126 |
* attempts to show the layout of the texture containing the two |
|
127 |
* GradientPaint colors (C1 and C2): |
|
128 |
* |
|
129 |
* +-----------------+ |
|
130 |
* | C1 | C2 | |
|
131 |
* | | | |
|
132 |
* +-----------------+ |
|
133 |
* u=0 .25 .5 .75 1 |
|
134 |
* |
|
135 |
* We calculate our plane equation constants (A,B,D) such that u=0.25 |
|
136 |
* corresponds to the first GradientPaint end point in user space and |
|
137 |
* u=0.75 corresponds to the second end point. This is somewhat |
|
138 |
* non-obvious, but since the gradient colors are generated by |
|
139 |
* interpolating between C1 and C2, we want the pure color at the |
|
140 |
* end points, and we will get the pure color only when u correlates |
|
141 |
* to the center of a texel. The following chart shows the expected |
|
142 |
* color for some sample values of u (where C' is the color halfway |
|
143 |
* between C1 and C2): |
|
144 |
* |
|
145 |
* u value acyclic (GL_CLAMP) cyclic (GL_REPEAT) |
|
146 |
* ------- ------------------ ------------------ |
|
147 |
* -0.25 C1 C2 |
|
148 |
* 0.0 C1 C' |
|
149 |
* 0.25 C1 C1 |
|
150 |
* 0.5 C' C' |
|
151 |
* 0.75 C2 C2 |
|
152 |
* 1.0 C2 C' |
|
153 |
* 1.25 C2 C1 |
|
154 |
* |
|
155 |
* Original inspiration for this technique came from UMD's Agile2D |
|
156 |
* project (GradientManager.java). |
|
157 |
*/ |
|
158 |
private static void setGradientPaint(RenderQueue rq, AffineTransform at, |
|
159 |
Color c1, Color c2, |
|
160 |
Point2D pt1, Point2D pt2, |
|
161 |
boolean isCyclic, boolean useMask) |
|
162 |
{ |
|
163 |
// convert gradient colors to IntArgbPre format |
|
164 |
PixelConverter pc = PixelConverter.ArgbPre.instance; |
|
165 |
int pixel1 = pc.rgbToPixel(c1.getRGB(), null); |
|
166 |
int pixel2 = pc.rgbToPixel(c2.getRGB(), null); |
|
167 |
||
168 |
// calculate plane equation constants |
|
169 |
double x = pt1.getX(); |
|
170 |
double y = pt1.getY(); |
|
171 |
at.translate(x, y); |
|
172 |
// now gradient point 1 is at the origin |
|
173 |
x = pt2.getX() - x; |
|
174 |
y = pt2.getY() - y; |
|
175 |
double len = Math.sqrt(x * x + y * y); |
|
176 |
at.rotate(x, y); |
|
177 |
// now gradient point 2 is on the positive x-axis |
|
178 |
at.scale(2*len, 1); |
|
179 |
// now gradient point 2 is at (0.5, 0) |
|
180 |
at.translate(-0.25, 0); |
|
181 |
// now gradient point 1 is at (0.25, 0), point 2 is at (0.75, 0) |
|
182 |
||
183 |
double p0, p1, p3; |
|
184 |
try { |
|
185 |
at.invert(); |
|
186 |
p0 = at.getScaleX(); |
|
187 |
p1 = at.getShearX(); |
|
188 |
p3 = at.getTranslateX(); |
|
189 |
} catch (java.awt.geom.NoninvertibleTransformException e) { |
|
190 |
p0 = p1 = p3 = 0.0; |
|
191 |
} |
|
192 |
||
193 |
// assert rq.lock.isHeldByCurrentThread(); |
|
194 |
rq.ensureCapacityAndAlignment(44, 12); |
|
195 |
RenderBuffer buf = rq.getBuffer(); |
|
196 |
buf.putInt(SET_GRADIENT_PAINT); |
|
197 |
buf.putInt(useMask ? 1 : 0); |
|
198 |
buf.putInt(isCyclic ? 1 : 0); |
|
199 |
buf.putDouble(p0).putDouble(p1).putDouble(p3); |
|
200 |
buf.putInt(pixel1).putInt(pixel2); |
|
201 |
} |
|
202 |
||
203 |
private static void setGradientPaint(RenderQueue rq, |
|
204 |
SunGraphics2D sg2d, |
|
205 |
GradientPaint paint, |
|
206 |
boolean useMask) |
|
207 |
{ |
|
208 |
setGradientPaint(rq, (AffineTransform)sg2d.transform.clone(), |
|
209 |
paint.getColor1(), paint.getColor2(), |
|
210 |
paint.getPoint1(), paint.getPoint2(), |
|
211 |
paint.isCyclic(), useMask); |
|
212 |
} |
|
213 |
||
214 |
/************************** TexturePaint support ****************************/ |
|
215 |
||
216 |
/** |
|
217 |
* We use OpenGL's texture coordinate generator to automatically |
|
218 |
* map the TexturePaint image to the geometry being rendered. The |
|
219 |
* generator uses two separate plane equations that take the (x,y) |
|
220 |
* location (in device space) of the fragment being rendered to |
|
221 |
* calculate (u,v) texture coordinates for that fragment: |
|
222 |
* u = Ax + By + Cz + Dw |
|
223 |
* v = Ex + Fy + Gz + Hw |
|
224 |
* |
|
225 |
* Since we use a 2D orthographic projection, we can assume that z=0 |
|
226 |
* and w=1 for any fragment. So we need to calculate appropriate |
|
227 |
* values for the plane equation constants (A,B,D) and (E,F,H) such |
|
228 |
* that {u,v}=0 for the top-left of the TexturePaint's anchor |
|
229 |
* rectangle and {u,v}=1 for the bottom-right of the anchor rectangle. |
|
230 |
* We can easily make the texture image repeat for {u,v} values |
|
231 |
* outside the range [0,1] by specifying the GL_REPEAT texture wrap |
|
232 |
* mode. |
|
233 |
* |
|
234 |
* Calculating the plane equation constants is surprisingly simple. |
|
235 |
* We can think of it as an inverse matrix operation that takes |
|
236 |
* device space coordinates and transforms them into user space |
|
237 |
* coordinates that correspond to a location relative to the anchor |
|
238 |
* rectangle. First, we translate and scale the current user space |
|
239 |
* transform by applying the anchor rectangle bounds. We then take |
|
240 |
* the inverse of this affine transform. The rows of the resulting |
|
241 |
* inverse matrix correlate nicely to the plane equation constants |
|
242 |
* we were seeking. |
|
243 |
*/ |
|
244 |
private static void setTexturePaint(RenderQueue rq, |
|
245 |
SunGraphics2D sg2d, |
|
246 |
TexturePaint paint, |
|
247 |
boolean useMask) |
|
248 |
{ |
|
249 |
BufferedImage bi = paint.getImage(); |
|
250 |
SurfaceData dstData = sg2d.surfaceData; |
|
251 |
SurfaceData srcData = |
|
11080
7e18e343964e
7117914: Fix javac warnings in src/share/classes/sun/java2d
neugens
parents:
7668
diff
changeset
|
252 |
dstData.getSourceSurfaceData(bi, SunGraphics2D.TRANSFORM_ISIDENT, |
2 | 253 |
CompositeType.SrcOver, null); |
254 |
boolean filter = |
|
255 |
(sg2d.interpolationType != |
|
256 |
AffineTransformOp.TYPE_NEAREST_NEIGHBOR); |
|
257 |
||
258 |
// calculate plane equation constants |
|
259 |
AffineTransform at = (AffineTransform)sg2d.transform.clone(); |
|
260 |
Rectangle2D anchor = paint.getAnchorRect(); |
|
261 |
at.translate(anchor.getX(), anchor.getY()); |
|
262 |
at.scale(anchor.getWidth(), anchor.getHeight()); |
|
263 |
||
264 |
double xp0, xp1, xp3, yp0, yp1, yp3; |
|
265 |
try { |
|
266 |
at.invert(); |
|
267 |
xp0 = at.getScaleX(); |
|
268 |
xp1 = at.getShearX(); |
|
269 |
xp3 = at.getTranslateX(); |
|
270 |
yp0 = at.getShearY(); |
|
271 |
yp1 = at.getScaleY(); |
|
272 |
yp3 = at.getTranslateY(); |
|
273 |
} catch (java.awt.geom.NoninvertibleTransformException e) { |
|
274 |
xp0 = xp1 = xp3 = yp0 = yp1 = yp3 = 0.0; |
|
275 |
} |
|
276 |
||
277 |
// assert rq.lock.isHeldByCurrentThread(); |
|
278 |
rq.ensureCapacityAndAlignment(68, 12); |
|
279 |
RenderBuffer buf = rq.getBuffer(); |
|
280 |
buf.putInt(SET_TEXTURE_PAINT); |
|
281 |
buf.putInt(useMask ? 1 : 0); |
|
282 |
buf.putInt(filter ? 1 : 0); |
|
283 |
buf.putLong(srcData.getNativeOps()); |
|
284 |
buf.putDouble(xp0).putDouble(xp1).putDouble(xp3); |
|
285 |
buf.putDouble(yp0).putDouble(yp1).putDouble(yp3); |
|
286 |
} |
|
287 |
||
288 |
/****************** Shared MultipleGradientPaint support ********************/ |
|
289 |
||
290 |
/** |
|
291 |
* The maximum number of gradient "stops" supported by our native |
|
292 |
* fragment shader implementations. |
|
293 |
* |
|
294 |
* This value has been empirically determined and capped to allow |
|
295 |
* our native shaders to run on all shader-level graphics hardware, |
|
296 |
* even on the older, more limited GPUs. Even the oldest Nvidia |
|
297 |
* hardware could handle 16, or even 32 fractions without any problem. |
|
298 |
* But the first-generation boards from ATI would fall back into |
|
299 |
* software mode (which is unusably slow) for values larger than 12; |
|
300 |
* it appears that those boards do not have enough native registers |
|
301 |
* to support the number of array accesses required by our gradient |
|
302 |
* shaders. So for now we will cap this value at 12, but we can |
|
303 |
* re-evaluate this in the future as hardware becomes more capable. |
|
304 |
*/ |
|
16734
da1901d79073
8000406: change files using @GenerateNativeHeader to use @Native
dxu
parents:
12813
diff
changeset
|
305 |
@Native public static final int MULTI_MAX_FRACTIONS = 12; |
2 | 306 |
|
307 |
/** |
|
308 |
* Helper function to convert a color component in sRGB space to |
|
309 |
* linear RGB space. Copied directly from the |
|
310 |
* MultipleGradientPaintContext class. |
|
311 |
*/ |
|
5579
1a5e995a710b
6307603: [X11] Use RENDER extension for complex operations done in software
ceisserer
parents:
2
diff
changeset
|
312 |
public static int convertSRGBtoLinearRGB(int color) { |
2 | 313 |
float input, output; |
314 |
||
315 |
input = color / 255.0f; |
|
316 |
if (input <= 0.04045f) { |
|
317 |
output = input / 12.92f; |
|
318 |
} else { |
|
319 |
output = (float)Math.pow((input + 0.055) / 1.055, 2.4); |
|
320 |
} |
|
321 |
||
322 |
return Math.round(output * 255.0f); |
|
323 |
} |
|
324 |
||
325 |
/** |
|
326 |
* Helper function to convert a (non-premultiplied) Color in sRGB |
|
327 |
* space to an IntArgbPre pixel value, optionally in linear RGB space. |
|
328 |
* Based on the PixelConverter.ArgbPre.rgbToPixel() method. |
|
329 |
*/ |
|
330 |
private static int colorToIntArgbPrePixel(Color c, boolean linear) { |
|
331 |
int rgb = c.getRGB(); |
|
332 |
if (!linear && ((rgb >> 24) == -1)) { |
|
333 |
return rgb; |
|
334 |
} |
|
335 |
int a = rgb >>> 24; |
|
336 |
int r = (rgb >> 16) & 0xff; |
|
337 |
int g = (rgb >> 8) & 0xff; |
|
338 |
int b = (rgb ) & 0xff; |
|
339 |
if (linear) { |
|
340 |
r = convertSRGBtoLinearRGB(r); |
|
341 |
g = convertSRGBtoLinearRGB(g); |
|
342 |
b = convertSRGBtoLinearRGB(b); |
|
343 |
} |
|
344 |
int a2 = a + (a >> 7); |
|
345 |
r = (r * a2) >> 8; |
|
346 |
g = (g * a2) >> 8; |
|
347 |
b = (b * a2) >> 8; |
|
348 |
return ((a << 24) | (r << 16) | (g << 8) | (b)); |
|
349 |
} |
|
350 |
||
351 |
/** |
|
352 |
* Converts the given array of Color objects into an int array |
|
353 |
* containing IntArgbPre pixel values. If the linear parameter |
|
354 |
* is true, the Color values will be converted into a linear RGB |
|
355 |
* color space before being returned. |
|
356 |
*/ |
|
357 |
private static int[] convertToIntArgbPrePixels(Color[] colors, |
|
358 |
boolean linear) |
|
359 |
{ |
|
360 |
int[] pixels = new int[colors.length]; |
|
361 |
for (int i = 0; i < colors.length; i++) { |
|
362 |
pixels[i] = colorToIntArgbPrePixel(colors[i], linear); |
|
363 |
} |
|
364 |
return pixels; |
|
365 |
} |
|
366 |
||
367 |
/********************** LinearGradientPaint support *************************/ |
|
368 |
||
369 |
/** |
|
370 |
* This method uses techniques that are nearly identical to those |
|
371 |
* employed in setGradientPaint() above. The primary difference |
|
372 |
* is that at the native level we use a fragment shader to manually |
|
373 |
* apply the plane equation constants to the current fragment position |
|
374 |
* to calculate the gradient position in the range [0,1] (the native |
|
375 |
* code for GradientPaint does the same, except that it uses OpenGL's |
|
376 |
* automatic texture coordinate generation facilities). |
|
377 |
* |
|
378 |
* One other minor difference worth mentioning is that |
|
379 |
* setGradientPaint() calculates the plane equation constants |
|
380 |
* such that the gradient end points are positioned at 0.25 and 0.75 |
|
381 |
* (for reasons discussed in the comments for that method). In |
|
382 |
* contrast, for LinearGradientPaint we setup the equation constants |
|
383 |
* such that the gradient end points fall at 0.0 and 1.0. The |
|
384 |
* reason for this difference is that in the fragment shader we |
|
385 |
* have more control over how the gradient values are interpreted |
|
386 |
* (depending on the paint's CycleMethod). |
|
387 |
*/ |
|
388 |
private static void setLinearGradientPaint(RenderQueue rq, |
|
389 |
SunGraphics2D sg2d, |
|
390 |
LinearGradientPaint paint, |
|
391 |
boolean useMask) |
|
392 |
{ |
|
393 |
boolean linear = |
|
394 |
(paint.getColorSpace() == ColorSpaceType.LINEAR_RGB); |
|
395 |
Color[] colors = paint.getColors(); |
|
396 |
int numStops = colors.length; |
|
397 |
Point2D pt1 = paint.getStartPoint(); |
|
398 |
Point2D pt2 = paint.getEndPoint(); |
|
399 |
AffineTransform at = paint.getTransform(); |
|
400 |
at.preConcatenate(sg2d.transform); |
|
401 |
||
402 |
if (!linear && numStops == 2 && |
|
403 |
paint.getCycleMethod() != CycleMethod.REPEAT) |
|
404 |
{ |
|
405 |
// delegate to the optimized two-color gradient codepath |
|
406 |
boolean isCyclic = |
|
407 |
(paint.getCycleMethod() != CycleMethod.NO_CYCLE); |
|
408 |
setGradientPaint(rq, at, |
|
409 |
colors[0], colors[1], |
|
410 |
pt1, pt2, |
|
411 |
isCyclic, useMask); |
|
412 |
return; |
|
413 |
} |
|
414 |
||
415 |
int cycleMethod = paint.getCycleMethod().ordinal(); |
|
416 |
float[] fractions = paint.getFractions(); |
|
417 |
int[] pixels = convertToIntArgbPrePixels(colors, linear); |
|
418 |
||
419 |
// calculate plane equation constants |
|
420 |
double x = pt1.getX(); |
|
421 |
double y = pt1.getY(); |
|
422 |
at.translate(x, y); |
|
423 |
// now gradient point 1 is at the origin |
|
424 |
x = pt2.getX() - x; |
|
425 |
y = pt2.getY() - y; |
|
426 |
double len = Math.sqrt(x * x + y * y); |
|
427 |
at.rotate(x, y); |
|
428 |
// now gradient point 2 is on the positive x-axis |
|
429 |
at.scale(len, 1); |
|
430 |
// now gradient point 1 is at (0.0, 0), point 2 is at (1.0, 0) |
|
431 |
||
432 |
float p0, p1, p3; |
|
433 |
try { |
|
434 |
at.invert(); |
|
435 |
p0 = (float)at.getScaleX(); |
|
436 |
p1 = (float)at.getShearX(); |
|
437 |
p3 = (float)at.getTranslateX(); |
|
438 |
} catch (java.awt.geom.NoninvertibleTransformException e) { |
|
439 |
p0 = p1 = p3 = 0.0f; |
|
440 |
} |
|
441 |
||
442 |
// assert rq.lock.isHeldByCurrentThread(); |
|
443 |
rq.ensureCapacity(20 + 12 + (numStops*4*2)); |
|
444 |
RenderBuffer buf = rq.getBuffer(); |
|
445 |
buf.putInt(SET_LINEAR_GRADIENT_PAINT); |
|
446 |
buf.putInt(useMask ? 1 : 0); |
|
447 |
buf.putInt(linear ? 1 : 0); |
|
448 |
buf.putInt(cycleMethod); |
|
449 |
buf.putInt(numStops); |
|
450 |
buf.putFloat(p0); |
|
451 |
buf.putFloat(p1); |
|
452 |
buf.putFloat(p3); |
|
453 |
buf.put(fractions); |
|
454 |
buf.put(pixels); |
|
455 |
} |
|
456 |
||
457 |
/********************** RadialGradientPaint support *************************/ |
|
458 |
||
459 |
/** |
|
460 |
* This method calculates six m** values and a focusX value that |
|
461 |
* are used by the native fragment shader. These techniques are |
|
462 |
* based on a whitepaper by Daniel Rice on radial gradient performance |
|
463 |
* (attached to the bug report for 6521533). One can refer to that |
|
464 |
* document for the complete set of formulas and calculations, but |
|
465 |
* the basic goal is to compose a transform that will convert an |
|
466 |
* (x,y) position in device space into a "u" value that represents |
|
467 |
* the relative distance to the gradient focus point. The resulting |
|
468 |
* value can be used to look up the appropriate color by linearly |
|
469 |
* interpolating between the two nearest colors in the gradient. |
|
470 |
*/ |
|
471 |
private static void setRadialGradientPaint(RenderQueue rq, |
|
472 |
SunGraphics2D sg2d, |
|
473 |
RadialGradientPaint paint, |
|
474 |
boolean useMask) |
|
475 |
{ |
|
476 |
boolean linear = |
|
477 |
(paint.getColorSpace() == ColorSpaceType.LINEAR_RGB); |
|
478 |
int cycleMethod = paint.getCycleMethod().ordinal(); |
|
479 |
float[] fractions = paint.getFractions(); |
|
480 |
Color[] colors = paint.getColors(); |
|
481 |
int numStops = colors.length; |
|
482 |
int[] pixels = convertToIntArgbPrePixels(colors, linear); |
|
483 |
Point2D center = paint.getCenterPoint(); |
|
484 |
Point2D focus = paint.getFocusPoint(); |
|
485 |
float radius = paint.getRadius(); |
|
486 |
||
487 |
// save original (untransformed) center and focus points |
|
488 |
double cx = center.getX(); |
|
489 |
double cy = center.getY(); |
|
490 |
double fx = focus.getX(); |
|
491 |
double fy = focus.getY(); |
|
492 |
||
493 |
// transform from gradient coords to device coords |
|
494 |
AffineTransform at = paint.getTransform(); |
|
495 |
at.preConcatenate(sg2d.transform); |
|
496 |
focus = at.transform(focus, focus); |
|
497 |
||
498 |
// transform unit circle to gradient coords; we start with the |
|
499 |
// unit circle (center=(0,0), focus on positive x-axis, radius=1) |
|
500 |
// and then transform into gradient space |
|
501 |
at.translate(cx, cy); |
|
502 |
at.rotate(fx - cx, fy - cy); |
|
503 |
at.scale(radius, radius); |
|
504 |
||
505 |
// invert to get mapping from device coords to unit circle |
|
506 |
try { |
|
507 |
at.invert(); |
|
508 |
} catch (Exception e) { |
|
509 |
at.setToScale(0.0, 0.0); |
|
510 |
} |
|
511 |
focus = at.transform(focus, focus); |
|
512 |
||
513 |
// clamp the focus point so that it does not rest on, or outside |
|
514 |
// of, the circumference of the gradient circle |
|
515 |
fx = Math.min(focus.getX(), 0.99); |
|
516 |
||
517 |
// assert rq.lock.isHeldByCurrentThread(); |
|
518 |
rq.ensureCapacity(20 + 28 + (numStops*4*2)); |
|
519 |
RenderBuffer buf = rq.getBuffer(); |
|
520 |
buf.putInt(SET_RADIAL_GRADIENT_PAINT); |
|
521 |
buf.putInt(useMask ? 1 : 0); |
|
522 |
buf.putInt(linear ? 1 : 0); |
|
523 |
buf.putInt(numStops); |
|
524 |
buf.putInt(cycleMethod); |
|
525 |
buf.putFloat((float)at.getScaleX()); |
|
526 |
buf.putFloat((float)at.getShearX()); |
|
527 |
buf.putFloat((float)at.getTranslateX()); |
|
528 |
buf.putFloat((float)at.getShearY()); |
|
529 |
buf.putFloat((float)at.getScaleY()); |
|
530 |
buf.putFloat((float)at.getTranslateY()); |
|
531 |
buf.putFloat((float)fx); |
|
532 |
buf.put(fractions); |
|
533 |
buf.put(pixels); |
|
534 |
} |
|
535 |
} |