/* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.java2d.jules; import java.awt.*; import java.awt.geom.*; import sun.awt.X11GraphicsEnvironment; import sun.java2d.pipe.*; import sun.java2d.xr.*; public class JulesPathBuf { static final double[] emptyDash = new double[0]; private static final byte CAIRO_PATH_OP_MOVE_TO = 0; private static final byte CAIRO_PATH_OP_LINE_TO = 1; private static final byte CAIRO_PATH_OP_CURVE_TO = 2; private static final byte CAIRO_PATH_OP_CLOSE_PATH = 3; private static final int CAIRO_FILL_RULE_WINDING = 0; private static final int CAIRO_FILL_RULE_EVEN_ODD = 1; GrowablePointArray points = new GrowablePointArray(128); GrowableByteArray ops = new GrowableByteArray(1, 128); int[] xTrapArray = new int[512]; private static final boolean isCairoAvailable; static { isCairoAvailable = java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Boolean>() { public Boolean run() { boolean loadSuccess = false; if (X11GraphicsEnvironment.isXRenderAvailable()) { try { System.loadLibrary("jules"); loadSuccess = true; if (X11GraphicsEnvironment.isXRenderVerbose()) { System.out.println( "Xrender: INFO: Jules library loaded"); } } catch (UnsatisfiedLinkError ex) { loadSuccess = false; if (X11GraphicsEnvironment.isXRenderVerbose()) { System.out.println( "Xrender: INFO: Jules library not installed."); } } } return Boolean.valueOf(loadSuccess); } }); } public static boolean isCairoAvailable() { return isCairoAvailable; } public TrapezoidList tesselateFill(Shape s, AffineTransform at, Region clip) { int windingRule = convertPathData(s, at); xTrapArray[0] = 0; xTrapArray = tesselateFillNative(points.getArray(), ops.getArray(), points.getSize(), ops.getSize(), xTrapArray, xTrapArray.length, getCairoWindingRule(windingRule), clip.getLoX(), clip.getLoY(), clip.getHiX(), clip.getHiY()); return new TrapezoidList(xTrapArray); } public TrapezoidList tesselateStroke(Shape s, BasicStroke bs, boolean thin, boolean adjust, boolean antialias, AffineTransform at, Region clip) { float lw; if (thin) { if (antialias) { lw = 0.5f; } else { lw = 1.0f; } } else { lw = bs.getLineWidth(); } convertPathData(s, at); double[] dashArray = floatToDoubleArray(bs.getDashArray()); xTrapArray[0] = 0; xTrapArray = tesselateStrokeNative(points.getArray(), ops.getArray(), points.getSize(), ops.getSize(), xTrapArray, xTrapArray.length, lw, bs.getEndCap(), bs.getLineJoin(), bs.getMiterLimit(), dashArray, dashArray.length, bs.getDashPhase(), 1, 0, 0, 0, 1, 0, clip.getLoX(), clip.getLoY(), clip.getHiX(), clip.getHiY()); return new TrapezoidList(xTrapArray); } protected double[] floatToDoubleArray(float[] dashArrayFloat) { double[] dashArrayDouble = emptyDash; if (dashArrayFloat != null) { dashArrayDouble = new double[dashArrayFloat.length]; for (int i = 0; i < dashArrayFloat.length; i++) { dashArrayDouble[i] = dashArrayFloat[i]; } } return dashArrayDouble; } protected int convertPathData(Shape s, AffineTransform at) { PathIterator pi = s.getPathIterator(at); double[] coords = new double[6]; double currX = 0; double currY = 0; while (!pi.isDone()) { int curOp = pi.currentSegment(coords); int pointIndex; switch (curOp) { case PathIterator.SEG_MOVETO: ops.addByte(CAIRO_PATH_OP_MOVE_TO); pointIndex = points.getNextIndex(); points.setX(pointIndex, DoubleToCairoFixed(coords[0])); points.setY(pointIndex, DoubleToCairoFixed(coords[1])); currX = coords[0]; currY = coords[1]; break; case PathIterator.SEG_LINETO: ops.addByte(CAIRO_PATH_OP_LINE_TO); pointIndex = points.getNextIndex(); points.setX(pointIndex, DoubleToCairoFixed(coords[0])); points.setY(pointIndex, DoubleToCairoFixed(coords[1])); currX = coords[0]; currY = coords[1]; break; /** * q0 = p0 * q1 = (p0+2*p1)/3 * q2 = (p2+2*p1)/3 * q3 = p2 */ case PathIterator.SEG_QUADTO: double x1 = coords[0]; double y1 = coords[1]; double x2, y2; double x3 = coords[2]; double y3 = coords[3]; x2 = x1 + (x3 - x1) / 3; y2 = y1 + (y3 - y1) / 3; x1 = currX + 2 * (x1 - currX) / 3; y1 =currY + 2 * (y1 - currY) / 3; ops.addByte(CAIRO_PATH_OP_CURVE_TO); pointIndex = points.getNextIndex(); points.setX(pointIndex, DoubleToCairoFixed(x1)); points.setY(pointIndex, DoubleToCairoFixed(y1)); pointIndex = points.getNextIndex(); points.setX(pointIndex, DoubleToCairoFixed(x2)); points.setY(pointIndex, DoubleToCairoFixed(y2)); pointIndex = points.getNextIndex(); points.setX(pointIndex, DoubleToCairoFixed(x3)); points.setY(pointIndex, DoubleToCairoFixed(y3)); currX = x3; currY = y3; break; case PathIterator.SEG_CUBICTO: ops.addByte(CAIRO_PATH_OP_CURVE_TO); pointIndex = points.getNextIndex(); points.setX(pointIndex, DoubleToCairoFixed(coords[0])); points.setY(pointIndex, DoubleToCairoFixed(coords[1])); pointIndex = points.getNextIndex(); points.setX(pointIndex, DoubleToCairoFixed(coords[2])); points.setY(pointIndex, DoubleToCairoFixed(coords[3])); pointIndex = points.getNextIndex(); points.setX(pointIndex, DoubleToCairoFixed(coords[4])); points.setY(pointIndex, DoubleToCairoFixed(coords[5])); currX = coords[4]; currY = coords[5]; break; case PathIterator.SEG_CLOSE: ops.addByte(CAIRO_PATH_OP_CLOSE_PATH); break; } pi.next(); } return pi.getWindingRule(); } private static native int[] tesselateStrokeNative(int[] pointArray, byte[] ops, int pointCnt, int opCnt, int[] xTrapArray, int xTrapArrayLength, double lineWidth, int lineCap, int lineJoin, double miterLimit, double[] dashArray, int dashCnt, double offset, double m00, double m01, double m02, double m10, double m11, double m12, int clipLowX, int clipLowY, int clipWidth, int clipHeight); private static native int[] tesselateFillNative(int[] pointArray, byte[] ops, int pointCnt, int opCnt, int[] xTrapArray, int xTrapArrayLength, int windingRule, int clipLowX, int clipLowY, int clipWidth, int clipHeight); public void clear() { points.clear(); ops.clear(); xTrapArray[0] = 0; } private static int DoubleToCairoFixed(double dbl) { return (int) (dbl * 256); } private static int getCairoWindingRule(int j2dWindingRule) { switch(j2dWindingRule) { case PathIterator.WIND_EVEN_ODD: return CAIRO_FILL_RULE_EVEN_ODD; case PathIterator.WIND_NON_ZERO: return CAIRO_FILL_RULE_WINDING; default: throw new IllegalArgumentException("Illegal Java2D winding rule specified"); } } }