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