--- old/src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java 2016-03-10 15:50:10.932337109 +0100 +++ new/src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java 2016-03-10 15:50:10.804337110 +0100 @@ -51,6 +51,9 @@ private static final float MIN_PEN_SIZE = 1f / NORM_SUBPIXELS; + static final float UPPER_BND = Float.MAX_VALUE / 2.0f; + static final float LOWER_BND = -UPPER_BND; + /** * Public constructor */ @@ -642,27 +645,128 @@ private static void pathToLoop(final float[] coords, final PathIterator pi, final PathConsumer2D pc2d) { + // ported from DuctusRenderingEngine.feedConsumer() + boolean pathClosed = false; + boolean skip = false; + boolean subpathStarted = false; + float mx = 0.0f; + float my = 0.0f; + for (; !pi.isDone(); pi.next()) { - switch (pi.currentSegment(coords)) { - case PathIterator.SEG_MOVETO: - pc2d.moveTo(coords[0], coords[1]); - continue; - case PathIterator.SEG_LINETO: - pc2d.lineTo(coords[0], coords[1]); - continue; - case PathIterator.SEG_QUADTO: - pc2d.quadTo(coords[0], coords[1], - coords[2], coords[3]); - continue; - case PathIterator.SEG_CUBICTO: - pc2d.curveTo(coords[0], coords[1], - coords[2], coords[3], - coords[4], coords[5]); - continue; - case PathIterator.SEG_CLOSE: + final int type = pi.currentSegment(coords); + if (pathClosed) { + pathClosed = false; + if (type != PathIterator.SEG_MOVETO) { + // Force current point back to last moveto point + pc2d.moveTo(mx, my); + subpathStarted = true; + } + } + switch (type) { + case PathIterator.SEG_MOVETO: + /* Checking SEG_MOVETO coordinates if they are out of the + * [LOWER_BND, UPPER_BND] range. This check also handles NaN + * and Infinity values. Skipping next path segment in case of + * invalid data. + */ + if (coords[0] < UPPER_BND && coords[0] > LOWER_BND && + coords[1] < UPPER_BND && coords[1] > LOWER_BND) + { + mx = coords[0]; + my = coords[1]; + pc2d.moveTo(mx, my); + subpathStarted = true; + skip = false; + } else { + skip = true; + } + break; + case PathIterator.SEG_LINETO: + /* Checking SEG_LINETO coordinates if they are out of the + * [LOWER_BND, UPPER_BND] range. This check also handles NaN + * and Infinity values. Ignoring current path segment in case + * of invalid data. If segment is skipped its endpoint + * (if valid) is used to begin new subpath. + */ + if (coords[0] < UPPER_BND && coords[0] > LOWER_BND && + coords[1] < UPPER_BND && coords[1] > LOWER_BND) + { + if (skip) { + pc2d.moveTo(coords[0], coords[1]); + subpathStarted = true; + skip = false; + } else { + pc2d.lineTo(coords[0], coords[1]); + } + } + break; + case PathIterator.SEG_QUADTO: + // Quadratic curves take two points + /* Checking SEG_QUADTO coordinates if they are out of the + * [LOWER_BND, UPPER_BND] range. This check also handles NaN + * and Infinity values. Ignoring current path segment in case + * of invalid endpoints's data. Equivalent to the SEG_LINETO + * if endpoint coordinates are valid but there are invalid data + * among other coordinates + */ + if (coords[2] < UPPER_BND && coords[2] > LOWER_BND && + coords[3] < UPPER_BND && coords[3] > LOWER_BND) + { + if (skip) { + pc2d.moveTo(coords[2], coords[3]); + subpathStarted = true; + skip = false; + } else { + if (coords[0] < UPPER_BND && coords[0] > LOWER_BND && + coords[1] < UPPER_BND && coords[1] > LOWER_BND) + { + pc2d.quadTo(coords[0], coords[1], + coords[2], coords[3]); + } else { + pc2d.lineTo(coords[2], coords[3]); + } + } + } + break; + case PathIterator.SEG_CUBICTO: + // Cubic curves take three points + /* Checking SEG_CUBICTO coordinates if they are out of the + * [LOWER_BND, UPPER_BND] range. This check also handles NaN + * and Infinity values. Ignoring current path segment in case + * of invalid endpoints's data. Equivalent to the SEG_LINETO + * if endpoint coordinates are valid but there are invalid data + * among other coordinates + */ + if (coords[4] < UPPER_BND && coords[4] > LOWER_BND && + coords[5] < UPPER_BND && coords[5] > LOWER_BND) + { + if (skip) { + pc2d.moveTo(coords[4], coords[5]); + subpathStarted = true; + skip = false; + } else { + if (coords[0] < UPPER_BND && coords[0] > LOWER_BND && + coords[1] < UPPER_BND && coords[1] > LOWER_BND && + coords[2] < UPPER_BND && coords[2] > LOWER_BND && + coords[3] < UPPER_BND && coords[3] > LOWER_BND) + { + pc2d.curveTo(coords[0], coords[1], + coords[2], coords[3], + coords[4], coords[5]); + } else { + pc2d.lineTo(coords[4], coords[5]); + } + } + } + break; + case PathIterator.SEG_CLOSE: + if (subpathStarted) { pc2d.closePath(); - continue; - default: + subpathStarted = false; + pathClosed = true; + } + break; + default: } } pc2d.pathDone(); --- old/src/java.desktop/share/classes/sun/java2d/marlin/Version.java 2016-03-10 15:50:11.388337103 +0100 +++ new/src/java.desktop/share/classes/sun/java2d/marlin/Version.java 2016-03-10 15:50:11.260337104 +0100 @@ -27,7 +27,7 @@ public final class Version { - private static final String version = "marlin-0.7.3.2-Unsafe-OpenJDK"; + private static final String version = "marlin-0.7.3.3-Unsafe-OpenJDK"; public static String getVersion() { return version; --- old/src/java.desktop/share/classes/sun/java2d/pipe/AAShapePipe.java 2016-03-10 15:50:11.820337097 +0100 +++ new/src/java.desktop/share/classes/sun/java2d/pipe/AAShapePipe.java 2016-03-10 15:50:11.692337098 +0100 @@ -28,7 +28,6 @@ import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.Rectangle2D; -import java.util.concurrent.ConcurrentLinkedQueue; import sun.awt.SunHints; import sun.java2d.ReentrantContext; import sun.java2d.ReentrantContextProvider; --- old/test/sun/java2d/marlin/CrashNaNTest.java 2016-03-10 15:50:12.212337091 +0100 +++ new/test/sun/java2d/marlin/CrashNaNTest.java 2016-03-10 15:50:12.076337093 +0100 @@ -26,6 +26,7 @@ import java.awt.RenderingHints; import java.awt.geom.Path2D; import java.awt.image.BufferedImage; +import java.awt.image.Raster; import java.io.File; import java.io.IOException; import static java.lang.Double.NaN; @@ -37,8 +38,9 @@ /** * @test - * @bug 8149338 - * @summary Verifies that Marlin supports NaN coordinates and no JVM crash happens ! + * @bug 8149338 8144938 + * @summary Verifies that Marlin supports NaN coordinates (no JVM crash) + * but also it skips properly point coordinates with NaN / Infinity values * @run main CrashNaNTest */ public class CrashNaNTest { @@ -92,8 +94,7 @@ g2d.clearRect(0, 0, width, height); final Path2D.Double path = new Path2D.Double(); - path.moveTo(30, 30); - path.lineTo(100, 100); + path.moveTo(100, 100); for (int i = 0; i < 20000; i++) { path.lineTo(110 + 0.01 * i, 110); @@ -105,20 +106,20 @@ path.lineTo(200, NaN); path.lineTo(300, 300); path.lineTo(NaN, NaN); - path.lineTo(100, 100); + path.lineTo(100, 200); path.closePath(); final Path2D.Double path2 = new Path2D.Double(); - path2.moveTo(0,0); - path2.lineTo(width,height); - path2.lineTo(10, 10); + path2.moveTo(0, 0); + path2.lineTo(100, height); + path2.lineTo(0, 200); path2.closePath(); for (int i = 0; i < 1; i++) { final long start = System.nanoTime(); g2d.setColor(Color.BLUE); g2d.fill(path); - + g2d.setColor(Color.GREEN); g2d.fill(path2); final long time = System.nanoTime() - start; @@ -136,8 +137,33 @@ ex.printStackTrace(); } } + + // Check image on few pixels: + final Raster raster = image.getData(); + + checkPixel(raster, 200, 195, Color.BLUE.getRGB()); + checkPixel(raster, 105, 195, Color.BLUE.getRGB()); + checkPixel(raster, 286, 290, Color.BLUE.getRGB()); + + checkPixel(raster, 108, 105, Color.WHITE.getRGB()); + checkPixel(raster, 205, 200, Color.WHITE.getRGB()); + + checkPixel(raster, 5, 200, Color.GREEN.getRGB()); + } finally { g2d.dispose(); } } + + private static void checkPixel(final Raster raster, + final int x, final int y, + final int expected) { + + final int[] rgb = (int[]) raster.getDataElements(x, y, null); + + if (rgb[0] != expected) { + throw new IllegalStateException("bad pixel at (" + x + ", " + y + + ") = " + rgb[0] + " expected: " + expected); + } + } }