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