/*
* 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");
}
}
}