1 /* 2 * Copyright (c) 2015, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.awt.BasicStroke; 25 import java.awt.Color; 26 import java.awt.Graphics2D; 27 import java.awt.RenderingHints; 28 import java.awt.geom.Path2D; 29 import static java.awt.geom.Path2D.WIND_NON_ZERO; 30 import java.awt.image.BufferedImage; 31 import java.io.File; 32 import java.io.IOException; 33 import javax.imageio.ImageIO; 34 import sun.java2d.pipe.RenderingEngine; 35 36 /** 37 * Simple crash rendering test using huge GeneralPaths with marlin renderer 38 * 39 * run it with large heap (2g): 40 * java -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine marlin.CrashTest 41 * 42 * @author bourgesl 43 */ 44 public class CrashTest { 45 46 static final boolean SAVE_IMAGE = false; 47 static boolean USE_ROUND_CAPS_AND_JOINS = true; 48 49 public static void main(String[] args) { 50 // try insane image sizes: 51 52 // subpixel coords may overflow: 53 // testHugeImage((Integer.MAX_VALUE >> 3) + 1, 6); 54 // larger than 23 bits: (RLE) 55 testHugeImage(8388608 + 1, 10); 56 57 test(0.1f, false, 0); 58 test(0.1f, true, 7f); 59 60 // Exceed 2Gb OffHeap buffer for edges: 61 try { 62 USE_ROUND_CAPS_AND_JOINS = true; 63 test(0.1f, true, 0.1f); 64 System.out.println("Exception MISSING."); 65 } 66 catch (Throwable th) { 67 if (th instanceof ArrayIndexOutOfBoundsException) { 68 System.out.println("ArrayIndexOutOfBoundsException expected."); 69 } else { 70 System.out.println("Exception occured:"); 71 th.printStackTrace(); 72 } 73 } 74 75 } 76 77 private static void test(final float lineStroke, 78 final boolean useDashes, 79 final float dashMinLen) 80 throws ArrayIndexOutOfBoundsException 81 { 82 System.out.println("---\n" + "test: " 83 + "lineStroke=" + lineStroke 84 + ", useDashes=" + useDashes 85 +", dashMinLen=" + dashMinLen 86 ); 87 88 final String renderer = RenderingEngine.getInstance().getClass().getSimpleName(); 89 System.out.println("Testing renderer = " + renderer); 90 91 final BasicStroke stroke = createStroke(lineStroke, useDashes, dashMinLen); 92 93 // TODO: test Dasher.firstSegmentsBuffer resizing ? 94 // array.dasher.firstSegmentsBuffer.d_float[2] sum: 6 avg: 3.0 [3 | 3] 95 /* 96 // Marlin growable arrays: 97 = new StatLong("array.dasher.firstSegmentsBuffer.d_float"); 98 = new StatLong("array.stroker.polystack.curves.d_float"); 99 = new StatLong("array.stroker.polystack.curveTypes.d_byte"); 100 = new StatLong("array.marlincache.rowAAChunk.d_byte"); 101 = new StatLong("array.marlincache.touchedTile.int"); 102 = new StatLong("array.renderer.alphaline.int"); 103 = new StatLong("array.renderer.crossings.int"); 104 = new StatLong("array.renderer.aux_crossings.int"); 105 = new StatLong("array.renderer.edgeBuckets.int"); 106 = new StatLong("array.renderer.edgeBucketCounts.int"); 107 = new StatLong("array.renderer.edgePtrs.int"); 108 = new StatLong("array.renderer.aux_edgePtrs.int"); 109 */ 110 // size > 8192 (exceed both tile and buckets arrays) 111 final int size = 9000; 112 System.out.println("image size = " + size); 113 114 final BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); 115 116 final Graphics2D g2d = (Graphics2D) image.getGraphics(); 117 try { 118 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 119 g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 120 121 g2d.setClip(0, 0, size, size); 122 g2d.setBackground(Color.WHITE); 123 g2d.clearRect(0, 0, size, size); 124 125 g2d.setStroke(stroke); 126 g2d.setColor(Color.BLACK); 127 128 final long start = System.nanoTime(); 129 130 paint(g2d, size - 10f); 131 132 final long time = System.nanoTime() - start; 133 134 System.out.println("paint: duration= " + (1e-6 * time) + " ms."); 135 136 if (SAVE_IMAGE) { 137 try { 138 final File file = new File("CrashTest-" + renderer + "-dash-" + useDashes + ".bmp"); 139 140 System.out.println("Writing file: " + file.getAbsolutePath()); 141 ImageIO.write(image, "BMP", file); 142 } catch (IOException ex) { 143 System.out.println("Writing file failure:"); 144 ex.printStackTrace(); 145 } 146 } 147 } finally { 148 g2d.dispose(); 149 } 150 } 151 152 private static void testHugeImage(final int width, final int height) 153 throws ArrayIndexOutOfBoundsException 154 { 155 System.out.println("---\n" + "testHugeImage: " 156 + "width=" + width 157 + ", height=" + height 158 ); 159 160 final String renderer = RenderingEngine.getInstance().getClass().getSimpleName(); 161 System.out.println("Testing renderer = " + renderer); 162 163 final BasicStroke stroke = createStroke(2.5f, false, 0); 164 165 // size > 24bits (exceed both tile and buckets arrays) 166 System.out.println("image size = " + width + " x "+height); 167 168 final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); 169 170 final Graphics2D g2d = (Graphics2D) image.getGraphics(); 171 try { 172 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 173 g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 174 175 g2d.setBackground(Color.WHITE); 176 g2d.clearRect(0, 0, width, height); 177 178 g2d.setStroke(stroke); 179 g2d.setColor(Color.BLACK); 180 181 final Path2D.Float path = new Path2D.Float(WIND_NON_ZERO, 32); 182 path.moveTo(0, 0); 183 path.lineTo(width, 0); 184 path.lineTo(width, height); 185 path.lineTo(0, height); 186 path.lineTo(0, 0); 187 188 final long start = System.nanoTime(); 189 190 g2d.draw(path); 191 192 final long time = System.nanoTime() - start; 193 194 System.out.println("paint: duration= " + (1e-6 * time) + " ms."); 195 196 if (SAVE_IMAGE) { 197 try { 198 final File file = new File("CrashTest-" + renderer + 199 "-huge-" + width + "x" +height + ".bmp"); 200 201 System.out.println("Writing file: " + file.getAbsolutePath()); 202 ImageIO.write(image, "BMP", file); 203 } catch (IOException ex) { 204 System.out.println("Writing file failure:"); 205 ex.printStackTrace(); 206 } 207 } 208 } finally { 209 g2d.dispose(); 210 } 211 } 212 213 private static void paint(final Graphics2D g2d, final float size) { 214 final double halfSize = size / 2.0; 215 216 final Path2D.Float path = new Path2D.Float(WIND_NON_ZERO, 32 * 1024); 217 218 // show cross: 219 path.moveTo(0, 0); 220 path.lineTo(size, size); 221 222 path.moveTo(size, 0); 223 path.lineTo(0, size); 224 225 path.moveTo(0, 0); 226 path.lineTo(size, 0); 227 228 path.moveTo(0, 0); 229 path.lineTo(0, size); 230 231 path.moveTo(0, 0); 232 233 double r = size; 234 235 final int ratio = 100; 236 int repeats = 1; 237 238 int n = 0; 239 240 while (r > 1.0) { 241 repeats *= ratio; 242 243 if (repeats > 10000) { 244 repeats = 10000; 245 } 246 247 for (int i = 0; i < repeats; i++) { 248 path.lineTo(halfSize - 0.5 * r + i * r / repeats, 249 halfSize - 0.5 * r); 250 n++; 251 path.lineTo(halfSize - 0.5 * r + i * r / repeats + 0.1, 252 halfSize + 0.5 * r); 253 n++; 254 } 255 256 r -= halfSize; 257 } 258 System.out.println("draw : " + n + " lines."); 259 g2d.draw(path); 260 } 261 262 private static BasicStroke createStroke(final float width, 263 final boolean useDashes, 264 final float dashMinLen) { 265 final float[] dashes; 266 267 if (useDashes) { 268 // huge dash array (exceed Dasher.INITIAL_ARRAY) 269 dashes = new float[512]; 270 271 float cur = dashMinLen; 272 float step = 0.01f; 273 274 for (int i = 0; i < dashes.length; i += 2) { 275 dashes[i] = cur; 276 dashes[i + 1] = cur; 277 cur += step; 278 } 279 } else { 280 dashes = null; 281 } 282 283 if (USE_ROUND_CAPS_AND_JOINS) { 284 // Use both round Caps & Joins: 285 return new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 100.0f, dashes, 0.0f); 286 } 287 return new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 100.0f, dashes, 0.0f); 288 } 289 }