1 /* 2 * Copyright (c) 2016, 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.PaintContext; 28 import java.awt.Rectangle; 29 import java.awt.RenderingHints; 30 import java.awt.TexturePaint; 31 import java.awt.geom.AffineTransform; 32 import java.awt.geom.Ellipse2D; 33 import java.awt.geom.Rectangle2D; 34 import java.awt.image.BufferedImage; 35 import java.awt.image.ColorModel; 36 import java.io.File; 37 import java.io.IOException; 38 import java.util.Locale; 39 import java.util.logging.Handler; 40 import java.util.logging.LogRecord; 41 import java.util.logging.Logger; 42 import javax.imageio.ImageIO; 43 44 /** 45 * @test 46 * @bug 8148886 47 * @summary Verifies that Marlin supports reentrant operations (ThreadLocal) 48 * like in custom Paint or custom Composite 49 * @run main CrashPaintTest 50 */ 51 public class CrashPaintTest { 52 53 static final boolean SAVE_IMAGE = false; 54 55 public static void main(String argv[]) { 56 Locale.setDefault(Locale.US); 57 58 // initialize j.u.l Looger: 59 final Logger log = Logger.getLogger("sun.java2d.marlin"); 60 log.addHandler(new Handler() { 61 @Override 62 public void publish(LogRecord record) { 63 Throwable th = record.getThrown(); 64 // detect potential Throwable thrown by XxxArrayCache.check(): 65 if (th != null && th.getClass() == Throwable.class) { 66 StackTraceElement[] stackElements = th.getStackTrace(); 67 68 for (int i = 0; i < stackElements.length; i++) { 69 StackTraceElement e = stackElements[i]; 70 71 if (e.getClassName().startsWith("sun.java2d.marlin") 72 && e.getClassName().contains("ArrayCache") 73 && "check".equals(e.getMethodName())) { 74 System.out.println("Test failed:\n" 75 + record.getMessage()); 76 th.printStackTrace(System.out); 77 78 throw new RuntimeException("Test failed: ", th); 79 } 80 } 81 } 82 } 83 84 @Override 85 public void flush() { 86 } 87 88 @Override 89 public void close() throws SecurityException { 90 } 91 }); 92 93 // enable Marlin logging & internal checks: 94 System.setProperty("sun.java2d.renderer.log", "true"); 95 System.setProperty("sun.java2d.renderer.useLogger", "true"); 96 System.setProperty("sun.java2d.renderer.doChecks", "true"); 97 98 // Force using thread-local storage: 99 System.setProperty("sun.java2d.renderer.useThreadLocal", "true"); 100 // Force smaller pixelsize to force using array caches: 101 System.setProperty("sun.java2d.renderer.pixelsize", "256"); 102 103 final int width = 300; 104 final int height = 300; 105 106 final BufferedImage image = new BufferedImage(width, height, 107 BufferedImage.TYPE_INT_ARGB); 108 109 final Graphics2D g2d = (Graphics2D) image.getGraphics(); 110 try { 111 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 112 RenderingHints.VALUE_ANTIALIAS_ON); 113 114 g2d.setBackground(Color.WHITE); 115 g2d.clearRect(0, 0, width, height); 116 117 final Ellipse2D.Double ellipse 118 = new Ellipse2D.Double(0, 0, width, height); 119 120 final long start = System.nanoTime(); 121 122 g2d.setPaint(new CustomPaint(100)); 123 g2d.fill(ellipse); 124 125 g2d.setColor(Color.GREEN); 126 g2d.setStroke(new BasicStroke(1f)); 127 g2d.draw(ellipse); 128 129 final long time = System.nanoTime() - start; 130 131 System.out.println("paint: duration= " + (1e-6 * time) + " ms."); 132 133 if (SAVE_IMAGE) { 134 try { 135 final File file = new File("CrashPaintTest.png"); 136 System.out.println("Writing file: " 137 + file.getAbsolutePath()); 138 ImageIO.write(image, "PNG", file); 139 } catch (IOException ex) { 140 System.out.println("Writing file failure:"); 141 ex.printStackTrace(); 142 } 143 } 144 } finally { 145 g2d.dispose(); 146 } 147 } 148 149 private static class CustomPaint extends TexturePaint { 150 private int size; 151 152 CustomPaint(final int size) { 153 super(new BufferedImage(size, size, 154 BufferedImage.TYPE_INT_ARGB), 155 new Rectangle2D.Double(0, 0, size, size) 156 ); 157 this.size = size; 158 } 159 160 @Override 161 public PaintContext createContext(ColorModel cm, 162 Rectangle deviceBounds, 163 Rectangle2D userBounds, 164 AffineTransform at, 165 RenderingHints hints) { 166 167 // Fill bufferedImage using 168 final Graphics2D g2d = (Graphics2D) getImage().getGraphics(); 169 try { 170 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 171 RenderingHints.VALUE_ANTIALIAS_ON); 172 g2d.setBackground(Color.PINK); 173 g2d.clearRect(0, 0, size, size); 174 175 g2d.setColor(Color.BLUE); 176 g2d.drawRect(0, 0, size, size); 177 178 g2d.fillOval(size / 10, size / 10, 179 size * 8 / 10, size * 8 / 10); 180 181 } finally { 182 g2d.dispose(); 183 } 184 185 return super.createContext(cm, deviceBounds, userBounds, at, hints); 186 } 187 } 188 }