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