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 }