--- old/src/java.desktop/share/classes/java/awt/geom/Path2D.java 2015-03-29 15:30:31.354639802 +0200 +++ new/src/java.desktop/share/classes/java/awt/geom/Path2D.java 2015-03-29 15:30:31.154639805 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2015, 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 @@ -224,8 +224,8 @@ Path2D p2d = (Path2D) s; setWindingRule(p2d.windingRule); this.numTypes = p2d.numTypes; - this.pointTypes = Arrays.copyOf(p2d.pointTypes, - p2d.pointTypes.length); + // trim arrays: + this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.numTypes); this.numCoords = p2d.numCoords; this.floatCoords = p2d.cloneCoordsFloat(at); } else { @@ -237,19 +237,23 @@ } } + @Override float[] cloneCoordsFloat(AffineTransform at) { + // trim arrays: float ret[]; if (at == null) { - ret = Arrays.copyOf(this.floatCoords, this.floatCoords.length); + ret = Arrays.copyOf(floatCoords, numCoords); } else { - ret = new float[floatCoords.length]; + ret = new float[numCoords]; at.transform(floatCoords, 0, ret, 0, numCoords / 2); } return ret; } + @Override double[] cloneCoordsDouble(AffineTransform at) { - double ret[] = new double[floatCoords.length]; + // trim arrays: + double ret[] = new double[numCoords]; if (at == null) { for (int i = 0; i < numCoords; i++) { ret[i] = floatCoords[i]; @@ -475,6 +479,9 @@ } int pointCrossings(double px, double py) { + if (numTypes == 0) { + return 0; + } double movx, movy, curx, cury, endx, endy; float coords[] = floatCoords; curx = movx = coords[0]; @@ -552,6 +559,9 @@ int rectCrossings(double rxmin, double rymin, double rxmax, double rymax) { + if (numTypes == 0) { + return 0; + } float coords[] = floatCoords; double curx, cury, movx, movy, endx, endy; curx = movx = coords[0]; @@ -1061,8 +1071,8 @@ Path2D p2d = (Path2D) s; setWindingRule(p2d.windingRule); this.numTypes = p2d.numTypes; - this.pointTypes = Arrays.copyOf(p2d.pointTypes, - p2d.pointTypes.length); + // trim arrays: + this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.numTypes); this.numCoords = p2d.numCoords; this.doubleCoords = p2d.cloneCoordsDouble(at); } else { @@ -1074,8 +1084,10 @@ } } + @Override float[] cloneCoordsFloat(AffineTransform at) { - float ret[] = new float[doubleCoords.length]; + // trim arrays: + float ret[] = new float[numCoords]; if (at == null) { for (int i = 0; i < numCoords; i++) { ret[i] = (float) doubleCoords[i]; @@ -1086,13 +1098,14 @@ return ret; } + @Override double[] cloneCoordsDouble(AffineTransform at) { + // trim arrays: double ret[]; if (at == null) { - ret = Arrays.copyOf(this.doubleCoords, - this.doubleCoords.length); + ret = Arrays.copyOf(doubleCoords, numCoords); } else { - ret = new double[doubleCoords.length]; + ret = new double[numCoords]; at.transform(doubleCoords, 0, ret, 0, numCoords / 2); } return ret; @@ -1202,6 +1215,9 @@ } int pointCrossings(double px, double py) { + if (numTypes == 0) { + return 0; + } double movx, movy, curx, cury, endx, endy; double coords[] = doubleCoords; curx = movx = coords[0]; @@ -1279,6 +1295,9 @@ int rectCrossings(double rxmin, double rymin, double rxmax, double rymax) { + if (numTypes == 0) { + return 0; + } double coords[] = doubleCoords; double curx, cury, movx, movy, endx, endy; curx = movx = coords[0]; --- /dev/null 2015-03-29 13:47:58.118572995 +0200 +++ new/test/java/awt/geom/Path2D/Path2DCopyConstructor.java 2015-03-29 15:30:31.650639798 +0200 @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2015, 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. + * + * 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. + */ + + +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.IllegalPathStateException; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +/** + * @test + * @bug 8042103 + * @summary Check Path2D copy constructor (trims arrays) + * and constructor with zero capacity + * @run main Path2DTrimCopy + */ +public class Path2DCopyConstructor { + + private final static float EPSILON = 5e-6f; + private final static float FLATNESS = 1e-2f; + + private final static AffineTransform at + = AffineTransform.getScaleInstance(1.3, 2.4); + + private final static Rectangle2D.Double rect2d + = new Rectangle2D.Double(3.2, 4.1, 5.0, 10.0); + + private final static Point2D.Double pt2d + = new Point2D.Double(2.0, 2.5); + + public static boolean verbose; + + static void log(String msg) { + if (verbose) { + System.out.println(msg); + } + } + + public static void main(String argv[]) { + verbose = (argv.length != 0); + + testEmptyDoublePaths(); + testDoublePaths(); + + testEmptyFloatPaths(); + testFloatPaths(); + + testEmptyGeneralPath(); + testGeneralPath(); + } + + static void testEmptyDoublePaths() { + log("\n - Test(Path2D.Double[0]) ---"); + test(() -> new Path2D.Double(Path2D.WIND_NON_ZERO, 0)); + } + + static void testDoublePaths() { + log("\n - Test(Path2D.Double) ---"); + test(() -> new Path2D.Double()); + } + + static void testEmptyFloatPaths() { + log("\n - Test(Path2D.Float[0]) ---"); + test(() -> new Path2D.Float(Path2D.WIND_NON_ZERO, 0)); + } + + static void testFloatPaths() { + log("\n - Test(Path2D.Float) ---"); + test(() -> new Path2D.Float()); + } + + static void testEmptyGeneralPath() { + log("\n - Test(GeneralPath[0]) ---"); + test(() -> new GeneralPath(Path2D.WIND_NON_ZERO, 0)); + } + + static void testGeneralPath() { + log("\n - Test(GeneralPath) ---"); + test(() -> new GeneralPath()); + } + + interface PathFactory { + Path2D makePath(); + } + + static void test(PathFactory pf) { + log("\n --- test: path(empty) ---"); + test(pf.makePath(), true); + log("\n\n --- test: path(addMove) ---"); + test(addMove(pf.makePath()), false); + log("\n\n --- test: path(addMoveAndLines) ---"); + test(addMoveAndLines(pf.makePath()), false); + log("\n\n --- test: path(addMoveAndQuads) ---"); + test(addMoveAndQuads(pf.makePath()), false); + log("\n\n --- test: path(addMoveAndCubics) ---"); + test(addMoveAndCubics(pf.makePath()), false); + log("\n\n --- test: path(addMoveAndClose) ---"); + test(addMoveAndClose(pf.makePath()), false); + } + + static Path2D addMove(Path2D p2d) { + p2d.moveTo(1.0, 0.5); + return p2d; + } + + static Path2D addMoveAndLines(Path2D p2d) { + addMove(p2d); + addLines(p2d); + return p2d; + } + + static Path2D addLines(Path2D p2d) { + for (int i = 0; i < 10; i++) { + p2d.lineTo(1.1 * i, 2.3 * i); + } + return p2d; + } + + static Path2D addMoveAndCubics(Path2D p2d) { + addMove(p2d); + addCubics(p2d); + return p2d; + } + + static Path2D addCubics(Path2D p2d) { + for (int i = 0; i < 10; i++) { + p2d.curveTo(1.1 * i, 1.2 * i, 1.3 * i, 1.4 * i, 1.5 * i, 1.6 * i); + } + return p2d; + } + + static Path2D addMoveAndQuads(Path2D p2d) { + addMove(p2d); + addQuads(p2d); + return p2d; + } + + static Path2D addQuads(Path2D p2d) { + for (int i = 0; i < 10; i++) { + p2d.quadTo(1.1 * i, 1.2 * i, 1.3 * i, 1.4 * i); + } + return p2d; + } + + static Path2D addMoveAndClose(Path2D p2d) { + addMove(p2d); + addClose(p2d); + return p2d; + } + + static Path2D addClose(Path2D p2d) { + p2d.closePath(); + return p2d; + } + + static void test(Path2D p2d, boolean isEmpty) { + testEqual(new Path2D.Float(p2d), p2d); + testEqual(new Path2D.Double(p2d), p2d); + testEqual(new GeneralPath(p2d), p2d); + + testIterator(new Path2D.Float(p2d), p2d); + testIterator(new Path2D.Double(p2d), p2d); + testIterator((Path2D) p2d.clone(), p2d); + + testFlattening(new Path2D.Float(p2d), p2d); + testFlattening(new Path2D.Double(p2d), p2d); + testFlattening((Path2D) p2d.clone(), p2d); + + testAddMove(new Path2D.Float(p2d)); + testAddMove(new Path2D.Double(p2d)); + testAddMove((Path2D) p2d.clone()); + + // These should expect exception if empty + testAddLine(new Path2D.Float(p2d), isEmpty); + testAddLine(new Path2D.Double(p2d), isEmpty); + testAddLine((Path2D) p2d.clone(), isEmpty); + + testAddQuad(new Path2D.Float(p2d), isEmpty); + testAddQuad(new Path2D.Double(p2d), isEmpty); + testAddQuad((Path2D) p2d.clone(), isEmpty); + + testAddCubic(new Path2D.Float(p2d), isEmpty); + testAddCubic(new Path2D.Double(p2d), isEmpty); + testAddCubic((Path2D) p2d.clone(), isEmpty); + + testAddClose(new Path2D.Float(p2d), isEmpty); + testAddClose(new Path2D.Double(p2d), isEmpty); + testAddClose((Path2D) p2d.clone(), isEmpty); + + testGetBounds(new Path2D.Float(p2d), p2d); + testGetBounds(new Path2D.Double(p2d), p2d); + testGetBounds((Path2D) p2d.clone(), p2d); + + testTransform(new Path2D.Float(p2d)); + testTransform(new Path2D.Double(p2d)); + testTransform((Path2D) p2d.clone()); + + testIntersect(new Path2D.Float(p2d), p2d); + testIntersect(new Path2D.Double(p2d), p2d); + testIntersect((Path2D) p2d.clone(), p2d); + + testContains(new Path2D.Float(p2d), p2d); + testContains(new Path2D.Double(p2d), p2d); + testContains((Path2D) p2d.clone(), p2d); + + testGetCurrentPoint(new Path2D.Float(p2d), p2d); + testGetCurrentPoint(new Path2D.Double(p2d), p2d); + testGetCurrentPoint((Path2D) p2d.clone(), p2d); + } + + static void testEqual(Path2D pathA, Path2D pathB) { + final PathIterator itA = pathA.getPathIterator(null); + final PathIterator itB = pathB.getPathIterator(null); + + float[] coordsA = new float[6]; + float[] coordsB = new float[6]; + + int n = 0; + for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) { + int typeA = itA.currentSegment(coordsA); + int typeB = itB.currentSegment(coordsB); + + if (typeA != typeB) { + throw new IllegalStateException("Path-segment[" + n + "] " + + " type are not equals [" + typeA + "|" + typeB + "] !"); + } + if (!equalsArray(coordsA, coordsB, getLength(typeA))) { + throw new IllegalStateException("Path-segment[" + n + "] coords" + + " are not equals [" + Arrays.toString(coordsA) + "|" + + Arrays.toString(coordsB) + "] !"); + } + } + if (!itA.isDone() || !itB.isDone()) { + throw new IllegalStateException("Paths do not have same lengths !"); + } + log("testEqual: " + n + " segments."); + } + + static void testIterator(Path2D pathA, Path2D pathB) { + final PathIterator itA = pathA.getPathIterator(at); + final PathIterator itB = pathB.getPathIterator(at); + + float[] coordsA = new float[6]; + float[] coordsB = new float[6]; + + int n = 0; + for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) { + int typeA = itA.currentSegment(coordsA); + int typeB = itB.currentSegment(coordsB); + + if (typeA != typeB) { + throw new IllegalStateException("Path-segment[" + n + "] " + + "type are not equals [" + typeA + "|" + typeB + "] !"); + } + // Take care of floating-point precision: + if (!equalsArrayEps(coordsA, coordsB, getLength(typeA))) { + throw new IllegalStateException("Path-segment[" + n + "] coords" + + " are not equals [" + Arrays.toString(coordsA) + "|" + + Arrays.toString(coordsB) + "] !"); + } + } + if (!itA.isDone() || !itB.isDone()) { + throw new IllegalStateException("Paths do not have same lengths !"); + } + log("testIterator: " + n + " segments."); + } + + static void testFlattening(Path2D pathA, Path2D pathB) { + final PathIterator itA = pathA.getPathIterator(at, FLATNESS); + final PathIterator itB = pathB.getPathIterator(at, FLATNESS); + + float[] coordsA = new float[6]; + float[] coordsB = new float[6]; + + int n = 0; + for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) { + int typeA = itA.currentSegment(coordsA); + int typeB = itB.currentSegment(coordsB); + + if (typeA != typeB) { + throw new IllegalStateException("Path-segment[" + n + "] " + + "type are not equals [" + typeA + "|" + typeB + "] !"); + } + // Take care of floating-point precision: + if (!equalsArrayEps(coordsA, coordsB, getLength(typeA))) { + throw new IllegalStateException("Path-segment[" + n + "] coords" + + " are not equals [" + Arrays.toString(coordsA) + "|" + + Arrays.toString(coordsB) + "] !"); + } + } + if (!itA.isDone() || !itB.isDone()) { + throw new IllegalStateException("Paths do not have same lengths !"); + } + log("testFlattening: " + n + " segments."); + } + + static void testAddMove(Path2D pathA) { + addMove(pathA); + log("testAddMove: passed."); + } + + static void testAddLine(Path2D pathA, boolean isEmpty) { + try { + addLines(pathA); + } + catch (IllegalPathStateException ipse) { + if (isEmpty) { + log("testAddLine: passed " + + "(expected IllegalPathStateException catched)."); + return; + } else { + throw ipse; + } + } + if (isEmpty) { + throw new IllegalStateException("IllegalPathStateException not thrown !"); + } + log("testAddLine: passed."); + } + + static void testAddQuad(Path2D pathA, boolean isEmpty) { + try { + addQuads(pathA); + } + catch (IllegalPathStateException ipse) { + if (isEmpty) { + log("testAddQuad: passed " + + "(expected IllegalPathStateException catched)."); + return; + } else { + throw ipse; + } + } + if (isEmpty) { + throw new IllegalStateException("IllegalPathStateException not thrown !"); + } + log("testAddQuad: passed."); + } + + static void testAddCubic(Path2D pathA, boolean isEmpty) { + try { + addCubics(pathA); + } + catch (IllegalPathStateException ipse) { + if (isEmpty) { + log("testAddCubic: passed " + + "(expected IllegalPathStateException catched)."); + return; + } else { + throw ipse; + } + } + if (isEmpty) { + throw new IllegalStateException("IllegalPathStateException not thrown !"); + } + log("testAddCubic: passed."); + } + + static void testAddClose(Path2D pathA, boolean isEmpty) { + try { + addClose(pathA); + } + catch (IllegalPathStateException ipse) { + if (isEmpty) { + log("testAddClose: passed " + + "(expected IllegalPathStateException catched)."); + return; + } else { + throw ipse; + } + } + if (isEmpty) { + throw new IllegalStateException("IllegalPathStateException not thrown !"); + } + log("testAddClose: passed."); + } + + static void testGetBounds(Path2D pathA, Path2D pathB) { + final Rectangle rA = pathA.getBounds(); + final Rectangle rB = pathB.getBounds(); + + if (!rA.equals(rB)) { + throw new IllegalStateException("Bounds are not equals [" + rA + + "|" + rB + "] !"); + } + final Rectangle2D r2dA = pathA.getBounds2D(); + final Rectangle2D r2dB = pathB.getBounds2D(); + + if (!equalsRectangle2D(r2dA, r2dB)) { + throw new IllegalStateException("Bounds2D are not equals [" + + r2dA + "|" + r2dB + "] !"); + } + log("testGetBounds: passed."); + } + + static void testTransform(Path2D pathA) { + pathA.transform(at); + log("testTransform: passed."); + } + + static void testIntersect(Path2D pathA, Path2D pathB) { + boolean resA = pathA.intersects(rect2d); + boolean resB = pathB.intersects(rect2d); + if (resA != resB) { + throw new IllegalStateException("Intersects(rect2d) are not equals [" + + resA + "|" + resB + "] !"); + } + resA = pathA.intersects(1.0, 2.0, 13.0, 17.0); + resB = pathB.intersects(1.0, 2.0, 13.0, 17.0); + if (resA != resB) { + throw new IllegalStateException("Intersects(doubles) are not equals [" + + resA + "|" + resB + "] !"); + } + log("testIntersect: passed."); + } + + static void testContains(Path2D pathA, Path2D pathB) { + boolean resA = pathA.contains(pt2d); + boolean resB = pathB.contains(pt2d); + if (resA != resB) { + throw new IllegalStateException("Contains(pt) are not equals [" + + resA + "|" + resB + "] !"); + } + resA = pathA.contains(pt2d.getX(), pt2d.getY()); + resB = pathB.contains(pt2d.getX(), pt2d.getY()); + if (resA != resB) { + throw new IllegalStateException("Contains(x,y) are not equals [" + + resA + "|" + resB + "] !"); + } + resA = pathA.contains(rect2d); + resB = pathB.contains(rect2d); + if (resA != resB) { + throw new IllegalStateException("Contains(rect2d) are not equals [" + + resA + "|" + resB + "] !"); + } + resA = pathA.contains(1.0, 2.0, 13.0, 17.0); + resB = pathB.contains(1.0, 2.0, 13.0, 17.0); + if (resA != resB) { + throw new IllegalStateException("Contains(doubles) are not equals [" + + resA + "|" + resB + "] !"); + } + log("testContains: passed."); + } + + static void testGetCurrentPoint(Path2D pathA, Path2D pathB) { + final Point2D ptA = pathA.getCurrentPoint(); + final Point2D ptB = pathA.getCurrentPoint(); + if (((ptA == null) && (ptB != null)) + || ((ptA != null) && !ptA.equals(ptB))) + { + throw new IllegalStateException("getCurrentPoint() are not equals [" + + ptA + "|" + ptB + "] !"); + } + log("testGetCurrentPoint: passed."); + } + + static int getLength(int type) { + switch(type) { + case PathIterator.SEG_CUBICTO: + return 6; + case PathIterator.SEG_QUADTO: + return 4; + case PathIterator.SEG_LINETO: + return 2; + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_CLOSE: + default: + return 0; + } + } + + + // Custom equals methods --- + + public static boolean equalsArray(float[] a, float[] a2, final int len) { + for (int i = 0; i < len; i++) { + if (Float.floatToIntBits(a[i]) != Float.floatToIntBits(a2[i])) { + return false; + } + } + return true; + } + + static boolean equalsArrayEps(float[] a, float[] a2, final int len) { + for (int i = 0; i < len; i++) { + if (!equalsEps(a[i], a2[i])) { + return false; + } + } + + return true; + } + + static boolean equalsRectangle2D(Rectangle2D a, Rectangle2D b) { + if (a == b) { + return true; + } + return equalsEps(a.getX(), b.getX()) + && equalsEps(a.getY(), b.getY()) + && equalsEps(a.getWidth(), b.getWidth()) + && equalsEps(a.getHeight(), b.getHeight()); + } + + static boolean equalsEps(float a, float b) { + return (Math.abs(a - b) <= EPSILON); + } + + static boolean equalsEps(double a, double b) { + return (Math.abs(a - b) <= EPSILON); + } +}