# HG changeset patch # User lbourges # Date 1525767201 -7200 # Node ID 793e481c7641ca4012b6fbf148ddd6a6b30b0f05 # Parent 6064cd8725fa433fc6bc1ba7a9dad394d93937fc 8202580: Dashed BasicStroke randomly painted incorrectly, may freeze application Summary: fixed Dasher.init() to use the correct part [0; dashLen[ Reviewed-by: prr, serb diff -r 6064cd8725fa -r 793e481c7641 src/java.desktop/share/classes/sun/java2d/marlin/DDasher.java --- a/src/java.desktop/share/classes/sun/java2d/marlin/DDasher.java Mon May 07 13:36:36 2018 -0700 +++ b/src/java.desktop/share/classes/sun/java2d/marlin/DDasher.java Tue May 08 10:13:21 2018 +0200 @@ -137,8 +137,8 @@ * @param recycleDashes true to indicate to recycle the given dash array * @return this instance */ - DDasher init(final DPathConsumer2D out, double[] dash, int dashLen, - double phase, boolean recycleDashes) + DDasher init(final DPathConsumer2D out, final double[] dash, final int dashLen, + double phase, final boolean recycleDashes) { this.out = out; @@ -146,9 +146,10 @@ int sidx = 0; dashOn = true; + // note: BasicStroke constructor checks dash elements and sum > 0 double sum = 0.0d; - for (double d : dash) { - sum += d; + for (int i = 0; i < dashLen; i++) { + sum += dash[i]; } this.cycleLen = sum; @@ -158,13 +159,13 @@ phase = 0.0d; } else { int fullcycles = FloatMath.floor_int(-cycles); - if ((fullcycles & dash.length & 1) != 0) { + if ((fullcycles & dashLen & 1) != 0) { dashOn = !dashOn; } phase += fullcycles * sum; while (phase < 0.0d) { if (--sidx < 0) { - sidx = dash.length - 1; + sidx = dashLen - 1; } phase += dash[sidx]; dashOn = !dashOn; @@ -175,14 +176,14 @@ phase = 0.0d; } else { int fullcycles = FloatMath.floor_int(cycles); - if ((fullcycles & dash.length & 1) != 0) { + if ((fullcycles & dashLen & 1) != 0) { dashOn = !dashOn; } phase -= fullcycles * sum; double d; while (phase >= (d = dash[sidx])) { phase -= d; - sidx = (sidx + 1) % dash.length; + sidx = (sidx + 1) % dashLen; dashOn = !dashOn; } } diff -r 6064cd8725fa -r 793e481c7641 src/java.desktop/share/classes/sun/java2d/marlin/Dasher.java --- a/src/java.desktop/share/classes/sun/java2d/marlin/Dasher.java Mon May 07 13:36:36 2018 -0700 +++ b/src/java.desktop/share/classes/sun/java2d/marlin/Dasher.java Tue May 08 10:13:21 2018 +0200 @@ -138,8 +138,8 @@ * @param recycleDashes true to indicate to recycle the given dash array * @return this instance */ - Dasher init(final PathConsumer2D out, float[] dash, int dashLen, - float phase, boolean recycleDashes) + Dasher init(final PathConsumer2D out, final float[] dash, final int dashLen, + float phase, final boolean recycleDashes) { this.out = out; @@ -147,9 +147,10 @@ int sidx = 0; dashOn = true; + // note: BasicStroke constructor checks dash elements and sum > 0 float sum = 0.0f; - for (float d : dash) { - sum += d; + for (int i = 0; i < dashLen; i++) { + sum += dash[i]; } this.cycleLen = sum; @@ -159,13 +160,13 @@ phase = 0.0f; } else { int fullcycles = FloatMath.floor_int(-cycles); - if ((fullcycles & dash.length & 1) != 0) { + if ((fullcycles & dashLen & 1) != 0) { dashOn = !dashOn; } phase += fullcycles * sum; while (phase < 0.0f) { if (--sidx < 0) { - sidx = dash.length - 1; + sidx = dashLen - 1; } phase += dash[sidx]; dashOn = !dashOn; @@ -176,14 +177,14 @@ phase = 0.0f; } else { int fullcycles = FloatMath.floor_int(cycles); - if ((fullcycles & dash.length & 1) != 0) { + if ((fullcycles & dashLen & 1) != 0) { dashOn = !dashOn; } phase -= fullcycles * sum; float d; while (phase >= (d = dash[sidx])) { phase -= d; - sidx = (sidx + 1) % dash.length; + sidx = (sidx + 1) % dashLen; dashOn = !dashOn; } } diff -r 6064cd8725fa -r 793e481c7641 test/jdk/sun/java2d/marlin/DashedRectTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/java2d/marlin/DashedRectTest.java Tue May 08 10:13:21 2018 +0200 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2018, 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.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import javax.imageio.ImageIO; + +/** + * Simple Dashed Rect rendering test + * + * @test + * @summary verify that dashed rectangle is properly rasterized + * @bug 8202580 + */ +public class DashedRectTest { + + static final boolean SAVE_IMAGE = false; + + private final static int N = 10; + + final static float DASH_LEN = 3.0f; + final static float DASH_PH = 5000f; + + final static int MAX = 100; + + public static void main(String[] args) { + + final int size = 200; + + // First display which renderer is tested: + // JDK9 only: + System.setProperty("sun.java2d.renderer.verbose", "true"); + + System.out.println("DashedRectClipTest: size = " + size); + + final BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); + + final Graphics2D g2d = (Graphics2D) image.getGraphics(); + try { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + + g2d.setClip(0, 0, size, size); + + g2d.setBackground(Color.WHITE); + g2d.clearRect(0, 0, size, size); + + // Corrupt Marlin Dasher.dash cached array: + g2d.setColor(Color.RED); + g2d.setStroke(createBadStroke()); + g2d.drawRect(20, 20, 50, 50); + + g2d.setStroke(createStroke()); + + g2d.setColor(Color.BLUE); + + for (int i = 0; i < N; i++) { + final long start = System.nanoTime(); + + g2d.drawRect(5, 5, MAX, MAX); + + final long time = System.nanoTime() - start; + + System.out.println("paint: duration= " + (1e-6 * time) + " ms."); + } + + if (SAVE_IMAGE) { + try { + final File file = new File("DashedRectClipTest-MAX-" + MAX + "-dashed.png"); + + System.out.println("Writing file: " + file.getAbsolutePath()); + ImageIO.write(image, "PNG", file); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + // Check image on few pixels: + final Raster raster = image.getData(); + + // 10, 5 = blue + checkPixel(raster, 10, 5, Color.BLUE.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); + } + } + + private static BasicStroke createStroke() { + return new BasicStroke(2f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, + new float[]{DASH_LEN}, DASH_PH) { + + }; + } + + private static BasicStroke createBadStroke() { + final float[] dash = new float[100]; + Arrays.fill(dash, 19.333f); + + return new BasicStroke(2f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, dash, DASH_PH); + } + +}