--- /dev/null 2015-11-23 13:25:31.000000000 -0800 +++ new/test/sun/java2d/marlin/CrashTest.java 2015-11-23 13:25:31.000000000 -0800 @@ -0,0 +1,289 @@ +/* + * 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.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.Path2D; +import static java.awt.geom.Path2D.WIND_NON_ZERO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; +import sun.java2d.pipe.RenderingEngine; + +/** + * Simple crash rendering test using huge GeneralPaths with marlin renderer + * + * run it with large heap (2g): + * java -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine marlin.CrashTest + * + * @author bourgesl + */ +public class CrashTest { + + static final boolean SAVE_IMAGE = false; + static boolean USE_ROUND_CAPS_AND_JOINS = true; + + public static void main(String[] args) { + // try insane image sizes: + + // subpixel coords may overflow: +// testHugeImage((Integer.MAX_VALUE >> 3) + 1, 6); + // larger than 23 bits: (RLE) + testHugeImage(8388608 + 1, 10); + + test(0.1f, false, 0); + test(0.1f, true, 7f); + + // Exceed 2Gb OffHeap buffer for edges: + try { + USE_ROUND_CAPS_AND_JOINS = true; + test(0.1f, true, 0.1f); + System.out.println("Exception MISSING."); + } + catch (Throwable th) { + if (th instanceof ArrayIndexOutOfBoundsException) { + System.out.println("ArrayIndexOutOfBoundsException expected."); + } else { + System.out.println("Exception occured:"); + th.printStackTrace(); + } + } + + } + + private static void test(final float lineStroke, + final boolean useDashes, + final float dashMinLen) + throws ArrayIndexOutOfBoundsException + { + System.out.println("---\n" + "test: " + + "lineStroke=" + lineStroke + + ", useDashes=" + useDashes + +", dashMinLen=" + dashMinLen + ); + + final String renderer = RenderingEngine.getInstance().getClass().getSimpleName(); + System.out.println("Testing renderer = " + renderer); + + final BasicStroke stroke = createStroke(lineStroke, useDashes, dashMinLen); + + // TODO: test Dasher.firstSegmentsBuffer resizing ? +// array.dasher.firstSegmentsBuffer.d_float[2] sum: 6 avg: 3.0 [3 | 3] + /* + // Marlin growable arrays: + = new StatLong("array.dasher.firstSegmentsBuffer.d_float"); + = new StatLong("array.stroker.polystack.curves.d_float"); + = new StatLong("array.stroker.polystack.curveTypes.d_byte"); + = new StatLong("array.marlincache.rowAAChunk.d_byte"); + = new StatLong("array.marlincache.touchedTile.int"); + = new StatLong("array.renderer.alphaline.int"); + = new StatLong("array.renderer.crossings.int"); + = new StatLong("array.renderer.aux_crossings.int"); + = new StatLong("array.renderer.edgeBuckets.int"); + = new StatLong("array.renderer.edgeBucketCounts.int"); + = new StatLong("array.renderer.edgePtrs.int"); + = new StatLong("array.renderer.aux_edgePtrs.int"); + */ + // size > 8192 (exceed both tile and buckets arrays) + final int size = 9000; + System.out.println("image 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_ON); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + g2d.setClip(0, 0, size, size); + g2d.setBackground(Color.WHITE); + g2d.clearRect(0, 0, size, size); + + g2d.setStroke(stroke); + g2d.setColor(Color.BLACK); + + final long start = System.nanoTime(); + + paint(g2d, size - 10f); + + final long time = System.nanoTime() - start; + + System.out.println("paint: duration= " + (1e-6 * time) + " ms."); + + if (SAVE_IMAGE) { + try { + final File file = new File("CrashTest-" + renderer + "-dash-" + useDashes + ".bmp"); + + System.out.println("Writing file: " + file.getAbsolutePath()); + ImageIO.write(image, "BMP", file); + } catch (IOException ex) { + System.out.println("Writing file failure:"); + ex.printStackTrace(); + } + } + } finally { + g2d.dispose(); + } + } + + private static void testHugeImage(final int width, final int height) + throws ArrayIndexOutOfBoundsException + { + System.out.println("---\n" + "testHugeImage: " + + "width=" + width + + ", height=" + height + ); + + final String renderer = RenderingEngine.getInstance().getClass().getSimpleName(); + System.out.println("Testing renderer = " + renderer); + + final BasicStroke stroke = createStroke(2.5f, false, 0); + + // size > 24bits (exceed both tile and buckets arrays) + System.out.println("image size = " + width + " x "+height); + + final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + + final Graphics2D g2d = (Graphics2D) image.getGraphics(); + try { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + g2d.setBackground(Color.WHITE); + g2d.clearRect(0, 0, width, height); + + g2d.setStroke(stroke); + g2d.setColor(Color.BLACK); + + final Path2D.Float path = new Path2D.Float(WIND_NON_ZERO, 32); + path.moveTo(0, 0); + path.lineTo(width, 0); + path.lineTo(width, height); + path.lineTo(0, height); + path.lineTo(0, 0); + + final long start = System.nanoTime(); + + g2d.draw(path); + + final long time = System.nanoTime() - start; + + System.out.println("paint: duration= " + (1e-6 * time) + " ms."); + + if (SAVE_IMAGE) { + try { + final File file = new File("CrashTest-" + renderer + + "-huge-" + width + "x" +height + ".bmp"); + + System.out.println("Writing file: " + file.getAbsolutePath()); + ImageIO.write(image, "BMP", file); + } catch (IOException ex) { + System.out.println("Writing file failure:"); + ex.printStackTrace(); + } + } + } finally { + g2d.dispose(); + } + } + + private static void paint(final Graphics2D g2d, final float size) { + final double halfSize = size / 2.0; + + final Path2D.Float path = new Path2D.Float(WIND_NON_ZERO, 32 * 1024); + + // show cross: + path.moveTo(0, 0); + path.lineTo(size, size); + + path.moveTo(size, 0); + path.lineTo(0, size); + + path.moveTo(0, 0); + path.lineTo(size, 0); + + path.moveTo(0, 0); + path.lineTo(0, size); + + path.moveTo(0, 0); + + double r = size; + + final int ratio = 100; + int repeats = 1; + + int n = 0; + + while (r > 1.0) { + repeats *= ratio; + + if (repeats > 10000) { + repeats = 10000; + } + + for (int i = 0; i < repeats; i++) { + path.lineTo(halfSize - 0.5 * r + i * r / repeats, + halfSize - 0.5 * r); + n++; + path.lineTo(halfSize - 0.5 * r + i * r / repeats + 0.1, + halfSize + 0.5 * r); + n++; + } + + r -= halfSize; + } + System.out.println("draw : " + n + " lines."); + g2d.draw(path); + } + + private static BasicStroke createStroke(final float width, + final boolean useDashes, + final float dashMinLen) { + final float[] dashes; + + if (useDashes) { + // huge dash array (exceed Dasher.INITIAL_ARRAY) + dashes = new float[512]; + + float cur = dashMinLen; + float step = 0.01f; + + for (int i = 0; i < dashes.length; i += 2) { + dashes[i] = cur; + dashes[i + 1] = cur; + cur += step; + } + } else { + dashes = null; + } + + if (USE_ROUND_CAPS_AND_JOINS) { + // Use both round Caps & Joins: + return new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 100.0f, dashes, 0.0f); + } + return new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 100.0f, dashes, 0.0f); + } +}