History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/java2d/pipe/BufferedPaints.java (1 of 18352 files). Show all

Unified · Split

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