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+
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+
49+public 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+}