1/*
2 * Copyright (c) 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.jules;
27
28import java.awt.*;
29import java.awt.geom.*;
30import sun.awt.X11GraphicsEnvironment;
31import sun.java2d.pipe.*;
32import sun.java2d.xr.*;
33
34public class JulesPathBuf {
35    static final double[] emptyDash = new double[0];
36
37    private static final byte CAIRO_PATH_OP_MOVE_TO = 0;
38    private static final byte CAIRO_PATH_OP_LINE_TO = 1;
39    private static final byte CAIRO_PATH_OP_CURVE_TO = 2;
40    private static final byte CAIRO_PATH_OP_CLOSE_PATH = 3;
41
42    private static final int  CAIRO_FILL_RULE_WINDING = 0;
43    private static final int CAIRO_FILL_RULE_EVEN_ODD = 1;
44
45    GrowablePointArray points = new GrowablePointArray(128);
46    GrowableByteArray ops = new GrowableByteArray(1, 128);
47    int[] xTrapArray = new int[512];
48
49    private static final boolean isCairoAvailable;
50
51    static {
52        isCairoAvailable =
53           java.security.AccessController.doPrivileged(
54                          new java.security.PrivilegedAction<Boolean>() {
55            public Boolean run() {
56                boolean loadSuccess = false;
57                if (X11GraphicsEnvironment.isXRenderAvailable()) {
58                    try {
59                        System.loadLibrary("jules");
60                        loadSuccess = true;
61                        if (X11GraphicsEnvironment.isXRenderVerbose()) {
62                            System.out.println(
63                                       "Xrender: INFO: Jules library loaded");
64                        }
65                    } catch (UnsatisfiedLinkError ex) {
66                        loadSuccess = false;
67                        if (X11GraphicsEnvironment.isXRenderVerbose()) {
68                            System.out.println(
69                                "Xrender: INFO: Jules library not installed.");
70                        }
71                    }
72                }
73                return Boolean.valueOf(loadSuccess);
74            }
75        });
76    }
77
78    public static boolean isCairoAvailable() {
79        return isCairoAvailable;
80    }
81
82    public TrapezoidList tesselateFill(Shape s, AffineTransform at, Region clip) {
83        int windingRule = convertPathData(s, at);
84        xTrapArray[0] = 0;
85
86        xTrapArray = tesselateFillNative(points.getArray(), ops.getArray(),
87                                         points.getSize(), ops.getSize(),
88                                         xTrapArray, xTrapArray.length,
89                                         getCairoWindingRule(windingRule),
90                                         clip.getLoX(), clip.getLoY(),
91                                         clip.getHiX(), clip.getHiY());
92
93        return new TrapezoidList(xTrapArray);
94    }
95
96    public TrapezoidList tesselateStroke(Shape s, BasicStroke bs, boolean thin,
97                                         boolean adjust, boolean antialias,
98                                         AffineTransform at, Region clip) {
99
100        float lw;
101        if (thin) {
102            if (antialias) {
103                lw = 0.5f;
104            } else {
105                lw = 1.0f;
106            }
107        } else {
108            lw = bs.getLineWidth();
109        }
110
111        convertPathData(s, at);
112
113        double[] dashArray = floatToDoubleArray(bs.getDashArray());
114        xTrapArray[0] = 0;
115
116        xTrapArray =
117             tesselateStrokeNative(points.getArray(), ops.getArray(),
118                                   points.getSize(), ops.getSize(),
119                                   xTrapArray, xTrapArray.length, lw,
120                                   bs.getEndCap(), bs.getLineJoin(),
121                                   bs.getMiterLimit(), dashArray,
122                                   dashArray.length, bs.getDashPhase(),
123                                   1, 0, 0, 0, 1, 0,
124                                   clip.getLoX(), clip.getLoY(),
125                                   clip.getHiX(), clip.getHiY());
126
127        return new TrapezoidList(xTrapArray);
128    }
129
130    protected double[] floatToDoubleArray(float[] dashArrayFloat) {
131        double[] dashArrayDouble = emptyDash;
132        if (dashArrayFloat != null) {
133            dashArrayDouble = new double[dashArrayFloat.length];
134
135            for (int i = 0; i < dashArrayFloat.length; i++) {
136                dashArrayDouble[i] = dashArrayFloat[i];
137            }
138        }
139
140        return dashArrayDouble;
141    }
142
143    protected int convertPathData(Shape s, AffineTransform at) {
144        PathIterator pi = s.getPathIterator(at);
145
146        double[] coords = new double[6];
147        double currX = 0;
148        double currY = 0;
149
150        while (!pi.isDone()) {
151            int curOp = pi.currentSegment(coords);
152
153            int pointIndex;
154            switch (curOp) {
155
156            case PathIterator.SEG_MOVETO:
157                ops.addByte(CAIRO_PATH_OP_MOVE_TO);
158                pointIndex = points.getNextIndex();
159                points.setX(pointIndex, DoubleToCairoFixed(coords[0]));
160                points.setY(pointIndex, DoubleToCairoFixed(coords[1]));
161                currX = coords[0];
162                currY = coords[1];
163                break;
164
165            case PathIterator.SEG_LINETO:
166                ops.addByte(CAIRO_PATH_OP_LINE_TO);
167                pointIndex = points.getNextIndex();
168                points.setX(pointIndex, DoubleToCairoFixed(coords[0]));
169                points.setY(pointIndex, DoubleToCairoFixed(coords[1]));
170                currX = coords[0];
171                currY = coords[1];
172                break;
173
174                /**
175                 *    q0 = p0
176                 *    q1 = (p0+2*p1)/3
177                 *    q2 = (p2+2*p1)/3
178                 *    q3 = p2
179                 */
180            case PathIterator.SEG_QUADTO:
181                double x1 = coords[0];
182                double y1 = coords[1];
183                double x2, y2;
184                double x3 = coords[2];
185                double y3 = coords[3];
186
187                x2 = x1 + (x3 - x1) / 3;
188                y2 = y1 + (y3 - y1) / 3;
189                x1 = currX + 2 * (x1 - currX) / 3;
190                y1 =currY + 2 * (y1 - currY) / 3;
191
192                ops.addByte(CAIRO_PATH_OP_CURVE_TO);
193                pointIndex = points.getNextIndex();
194                points.setX(pointIndex, DoubleToCairoFixed(x1));
195                points.setY(pointIndex, DoubleToCairoFixed(y1));
196                pointIndex = points.getNextIndex();
197                points.setX(pointIndex, DoubleToCairoFixed(x2));
198                points.setY(pointIndex, DoubleToCairoFixed(y2));
199                pointIndex = points.getNextIndex();
200                points.setX(pointIndex, DoubleToCairoFixed(x3));
201                points.setY(pointIndex, DoubleToCairoFixed(y3));
202                currX = x3;
203                currY = y3;
204                break;
205
206            case PathIterator.SEG_CUBICTO:
207                ops.addByte(CAIRO_PATH_OP_CURVE_TO);
208                pointIndex = points.getNextIndex();
209                points.setX(pointIndex, DoubleToCairoFixed(coords[0]));
210                points.setY(pointIndex, DoubleToCairoFixed(coords[1]));
211                pointIndex = points.getNextIndex();
212                points.setX(pointIndex, DoubleToCairoFixed(coords[2]));
213                points.setY(pointIndex, DoubleToCairoFixed(coords[3]));
214                pointIndex = points.getNextIndex();
215                points.setX(pointIndex, DoubleToCairoFixed(coords[4]));
216                points.setY(pointIndex, DoubleToCairoFixed(coords[5]));
217                currX = coords[4];
218                currY = coords[5];
219                break;
220
221            case PathIterator.SEG_CLOSE:
222                ops.addByte(CAIRO_PATH_OP_CLOSE_PATH);
223                break;
224            }
225
226            pi.next();
227        }
228
229        return pi.getWindingRule();
230    }
231
232    private static native int[]
233         tesselateStrokeNative(int[] pointArray, byte[] ops,
234                               int pointCnt, int opCnt,
235                               int[] xTrapArray, int xTrapArrayLength,
236                               double lineWidth, int lineCap, int lineJoin,
237                               double miterLimit, double[] dashArray,
238                               int dashCnt, double offset,
239                               double m00, double m01, double m02,
240                               double m10, double m11, double m12,
241                               int clipLowX, int clipLowY,
242                               int clipWidth, int clipHeight);
243
244    private static native int[]
245        tesselateFillNative(int[] pointArray, byte[] ops, int pointCnt,
246                            int opCnt, int[] xTrapArray, int xTrapArrayLength,
247                            int windingRule, int clipLowX, int clipLowY,                                    int clipWidth, int clipHeight);
248
249    public void clear() {
250        points.clear();
251        ops.clear();
252        xTrapArray[0] = 0;
253    }
254
255    private static int DoubleToCairoFixed(double dbl) {
256        return (int) (dbl * 256);
257    }
258
259    private static int getCairoWindingRule(int j2dWindingRule) {
260        switch(j2dWindingRule) {
261        case PathIterator.WIND_EVEN_ODD:
262            return CAIRO_FILL_RULE_EVEN_ODD;
263
264        case PathIterator.WIND_NON_ZERO:
265            return CAIRO_FILL_RULE_WINDING;
266
267            default:
268                throw new IllegalArgumentException("Illegal Java2D winding rule specified");
269        }
270    }
271}
272