1 /* 2 * Copyright (c) 2014, 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.AWTException; 25 import java.awt.Canvas; 26 import java.awt.Color; 27 import java.awt.Component; 28 import java.awt.Dimension; 29 import java.awt.Frame; 30 import java.awt.Graphics; 31 import java.awt.Graphics2D; 32 import java.awt.GraphicsConfiguration; 33 import java.awt.GraphicsEnvironment; 34 import java.awt.HeadlessException; 35 import java.awt.Rectangle; 36 import java.awt.Robot; 37 import java.awt.Toolkit; 38 import java.awt.event.WindowAdapter; 39 import java.awt.event.WindowEvent; 40 import java.awt.image.BufferedImage; 41 import java.awt.image.VolatileImage; 42 import java.io.File; 43 import java.io.IOException; 44 import java.util.HashSet; 45 import javax.imageio.ImageIO; 46 import sun.awt.ConstrainableGraphics; 47 48 /** 49 * @test 50 * @bug 6335200 6419610 51 * @summary Tests that we don't render anything if specific empty clip is set 52 * @author Dmitri.Trembovetski@Sun.COM: area=Graphics 53 * @modules java.desktop/sun.awt 54 * @run main EmptyClipRenderingTest 55 * @run main/othervm -Dsun.java2d.noddraw=true EmptyClipRenderingTest 56 * @run main/othervm -Dsun.java2d.pmoffscreen=true EmptyClipRenderingTest 57 * @run main/othervm -Dsun.java2d.opengl=true EmptyClipRenderingTest 58 */ 59 public class EmptyClipRenderingTest { 60 static final int IMG_W = 400; 61 static final int IMG_H = 400; 62 63 // generated rectangles 64 static HashSet<Rectangle> rects; 65 66 volatile boolean isActivated = false; 67 volatile boolean isPainted; 68 private static boolean showErrors = false; 69 70 public EmptyClipRenderingTest() { 71 // initialize clip/render region rectangles 72 initClips(); 73 74 HashSet<RuntimeException> errors = new HashSet<RuntimeException>(); 75 76 BufferedImage screenResult = testOnscreen(); 77 try { 78 testResult(screenResult, "Screen"); 79 } catch (RuntimeException e) { 80 errors.add(e); 81 } 82 83 BufferedImage destBI = 84 new BufferedImage(IMG_W, IMG_H, BufferedImage.TYPE_INT_RGB); 85 runTest((Graphics2D)destBI.getGraphics()); 86 try { 87 testResult(destBI, "BufferedImage"); 88 } catch (RuntimeException e) { 89 errors.add(e); 90 } 91 92 GraphicsConfiguration gc = 93 GraphicsEnvironment.getLocalGraphicsEnvironment(). 94 getDefaultScreenDevice().getDefaultConfiguration(); 95 VolatileImage destVI = gc.createCompatibleVolatileImage(IMG_W, IMG_H); 96 destVI.validate(gc); 97 runTest((Graphics2D)destVI.getGraphics()); 98 try { 99 testResult(destVI.getSnapshot(), "VolatileImage"); 100 } catch (RuntimeException e) { 101 errors.add(e); 102 } 103 104 if (errors.isEmpty()) { 105 System.err.println("Test PASSED."); 106 } else { 107 for (RuntimeException re : errors) { 108 re.printStackTrace(); 109 } 110 if (showErrors) { 111 System.err.println("Test FAILED: "+ errors.size() + 112 " subtest failures."); 113 } else { 114 throw new RuntimeException("Test FAILED: "+ errors.size() + 115 " subtest failures."); 116 } 117 } 118 } 119 120 /** 121 * Recursively adds 4 new rectangles: two vertical and two horizontal 122 * based on the passed rectangle area. The area is then shrunk and the 123 * process repeated for smaller area. 124 */ 125 private static void add4Rects(HashSet<Rectangle> rects, Rectangle area) { 126 if (area.width < 10 || area.height < 10) { 127 rects.add(area); 128 return; 129 } 130 // two vertical rects 131 rects.add(new Rectangle(area.x, area.y, 5, area.height)); 132 rects.add(new Rectangle(area.x + area.width - 5, area.y, 5, area.height)); 133 // two horizontal rects 134 int width = area.width - 2*(5 + 1); 135 rects.add(new Rectangle(area.x+6, area.y, width, 5)); 136 rects.add(new Rectangle(area.x+6, area.y + area.height - 5, width, 5)); 137 // reduce the area and repeat 138 area.grow(-6, -6); 139 add4Rects(rects, area); 140 } 141 142 /** 143 * Generate a bunch of non-intersecting rectangles 144 */ 145 private static void initClips() { 146 rects = new HashSet<Rectangle>(); 147 add4Rects(rects, new Rectangle(0, 0, IMG_W, IMG_H)); 148 System.err.println("Total number of test rects: " + rects.size()); 149 } 150 151 /** 152 * Render the pattern to the screen, capture the output with robot and 153 * return it. 154 */ 155 private BufferedImage testOnscreen() throws HeadlessException { 156 final Canvas destComponent; 157 final Object lock = new Object(); 158 Frame f = new Frame("Test Frame"); 159 f.setUndecorated(true); 160 f.add(destComponent = new Canvas() { 161 public void paint(Graphics g) { 162 isPainted = true; 163 } 164 public Dimension getPreferredSize() { 165 return new Dimension(IMG_W, IMG_H); 166 } 167 }); 168 f.addWindowListener(new WindowAdapter() { 169 public void windowActivated(WindowEvent e) { 170 if (!isActivated) { 171 synchronized (lock) { 172 isActivated = true; 173 lock.notify(); 174 } 175 } 176 } 177 }); 178 f.pack(); 179 f.setLocationRelativeTo(null); 180 f.setVisible(true); 181 synchronized(lock) { 182 while (!isActivated) { 183 try { 184 lock.wait(100); 185 } catch (InterruptedException ex) { 186 ex.printStackTrace(); 187 } 188 } 189 } 190 Robot r; 191 try { 192 r = new Robot(); 193 } catch (AWTException ex) { 194 throw new RuntimeException("Can't create Robot"); 195 } 196 BufferedImage bi; 197 int attempt = 0; 198 do { 199 if (++attempt > 10) { 200 throw new RuntimeException("Too many attempts: " + attempt); 201 } 202 isPainted = false; 203 runTest((Graphics2D) destComponent.getGraphics()); 204 r.waitForIdle(); 205 Toolkit.getDefaultToolkit().sync(); 206 bi = r.createScreenCapture( 207 new Rectangle(destComponent.getLocationOnScreen().x, 208 destComponent.getLocationOnScreen().y, 209 destComponent.getWidth(), 210 destComponent.getHeight())); 211 } while (isPainted); 212 f.setVisible(false); 213 f.dispose(); 214 return bi; 215 } 216 217 /** 218 * Run the test: cycle through all the rectangles, use one as clip and 219 * another as the area to render to. 220 * Set the clip in the same way Swing does it when repainting: 221 * first constrain the graphics to the damaged area, and repaint everything 222 */ 223 void runTest(Graphics2D destGraphics) { 224 destGraphics.setColor(Color.black); 225 destGraphics.fillRect(0, 0, IMG_W, IMG_H); 226 227 destGraphics.setColor(Color.red); 228 for (Rectangle clip : rects) { 229 Graphics2D g2d = (Graphics2D)destGraphics.create(); 230 g2d.setColor(Color.red); 231 // mimic what swing does in BufferStrategyPaintManager 232 if (g2d instanceof ConstrainableGraphics) { 233 ((ConstrainableGraphics)g2d).constrain(clip.x, clip.y, 234 clip.width, clip.height); 235 } 236 g2d.setClip(clip); 237 238 for (Rectangle renderRegion : rects) { 239 if (renderRegion != clip) { 240 // from CellRendererPane's paintComponent 241 Graphics2D rG = (Graphics2D) 242 g2d.create(renderRegion.x, renderRegion.y, 243 renderRegion.width, renderRegion.height); 244 rG.fillRect(0,0, renderRegion.width, renderRegion.height); 245 } 246 } 247 } 248 } 249 250 void testResult(final BufferedImage bi, final String desc) { 251 for (int y = 0; y < bi.getHeight(); y++) { 252 for (int x = 0; x < bi.getWidth(); x++) { 253 if (bi.getRGB(x, y) != Color.black.getRGB()) { 254 if (showErrors) { 255 Frame f = new Frame("Error: " + desc); 256 f.add(new Component() { 257 public void paint(Graphics g) { 258 g.drawImage(bi, 0, 0, null); 259 } 260 public Dimension getPreferredSize() { 261 return new Dimension(bi.getWidth(), 262 bi.getHeight()); 263 } 264 }); 265 f.pack(); 266 f.setVisible(true); 267 } 268 try { 269 String fileName = 270 "EmptyClipRenderingTest_"+desc+"_res.png"; 271 System.out.println("Writing resulting image: "+fileName); 272 ImageIO.write(bi, "png", new File(fileName)); 273 } catch (IOException ex) { 274 ex.printStackTrace(); 275 } 276 throw new RuntimeException("Dest: "+desc+ 277 " was rendered to at x="+ 278 x + " y=" + y + 279 " pixel="+Integer.toHexString(bi.getRGB(x,y))); 280 } 281 } 282 } 283 } 284 285 public static void main(String argv[]) { 286 for (String arg : argv) { 287 if (arg.equals("-show")) { 288 showErrors = true; 289 } else { 290 usage("Incorrect argument:" + arg); 291 } 292 } 293 new EmptyClipRenderingTest(); 294 } 295 296 private static void usage(String string) { 297 System.out.println(string); 298 System.out.println("Usage: EmptyClipRenderingTest [-show]"); 299 } 300 }