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