1/*
2 * Copyright (c) 2005, 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
24/*
25 * @test
26 * @key headful
27 * @bug 4832224 6322584 6328478 6328481 6322580 6588884 6587863
28 * @summary Verifies that the pixelization of simple primitives (drawLine,
29 * fillRect, drawRect, fill, draw) with the OGL pipeline enabled
30 * matches that produced by our software loops.  (The primitives tested here
31 * are simple enough that the OGL results should match the software results
32 * exactly.)  There is some overlap with PolyVertTest as we test both
33 * solid and XOR rendering here, but this testcase is a bit simpler and
34 * more appropriate for quick OGL testing.  This test is also useful for
35 * comparing quality between our X11/GDI and software pipelines.
36 * @run main/othervm SimplePrimQuality
37 * @run main/othervm -Dsun.java2d.opengl=True SimplePrimQuality
38 * @author campbelc
39 */
40
41import java.awt.*;
42import java.awt.geom.*;
43import java.awt.image.*;
44import java.io.File;
45import java.io.IOException;
46import javax.imageio.ImageIO;
47
48public class SimplePrimQuality extends Canvas {
49
50    private static final int SIZE = 300;
51    private static boolean done;
52    private static boolean testVI;
53    private static volatile BufferedImage capture;
54    private static void doCapture(Component test) {
55        // Grab the screen region
56        try {
57            Robot robot = new Robot();
58            Point pt1 = test.getLocationOnScreen();
59            Rectangle rect =
60                new Rectangle(pt1.x, pt1.y, test.getWidth(), test.getHeight());
61            capture = robot.createScreenCapture(rect);
62        } catch (Exception e) {
63            e.printStackTrace();
64        }
65    }
66
67    private static final int[][] rpts = {
68        {2, 0, 0, 0},
69        {12, 0, 1, 0},
70        {22, 0, 0, 1},
71        {32, 0, 1, 1},
72        {42, 0, 2, 1},
73        {52, 0, 1, 2},
74        {62, 0, 2, 2},
75        {72, 0, 5, 5},
76        {82, 0, 10, 10},
77        {97, 0, 15, 15},
78    };
79
80    private void drawLine(Graphics2D g, int x, int y, int dx, int dy) {
81        g.drawLine(x, y, x + dx, y + dy);
82    }
83
84    private void drawLines(Graphics2D g, int s) {
85        drawLine(g, 2, 0, 0, 0);
86        drawLine(g, 12, 0, 0, s);
87        drawLine(g, 22, 0, s, 0);
88        drawLine(g, 32, 0, s, s);
89        drawLine(g, 42, 0, 0, -s);
90        drawLine(g, 52, 0, -s, 0);
91        drawLine(g, 62, 0, -s, -s);
92        drawLine(g, 72, 0, -s, s);
93        drawLine(g, 82, 0, s, -s);
94    }
95
96    private void fillRects(Graphics2D g) {
97        for (int i = 0; i < rpts.length; i++) {
98            g.fillRect(rpts[i][0], rpts[i][1], rpts[i][2], rpts[i][3]);
99        }
100    }
101
102    private void drawRects(Graphics2D g) {
103        for (int i = 0; i < rpts.length; i++) {
104            g.drawRect(rpts[i][0], rpts[i][1], rpts[i][2], rpts[i][3]);
105        }
106    }
107
108    private void fillOvals(Graphics2D g) {
109        for (int i = 0; i < rpts.length; i++) {
110            // use fill() instead of fillOval(), since the former is more
111            // likely to be consistent with our software loops when the
112            // OGL pipeline cannot be enabled
113            g.fill(new Ellipse2D.Float(rpts[i][0], rpts[i][1],
114                                       rpts[i][2], rpts[i][3]));
115        }
116    }
117
118    private void drawOvals(Graphics2D g) {
119        for (int i = 0; i < rpts.length; i++) {
120            // use draw() instead of drawOval(), since the former is more
121            // likely to be consistent with our software loops when the
122            // OGL pipeline cannot be enabled
123            g.draw(new Ellipse2D.Float(rpts[i][0], rpts[i][1],
124                                       rpts[i][2], rpts[i][3]));
125        }
126    }
127
128    private void renderShapes(Graphics2D g) {
129        // drawLine tests...
130        g.translate(0, 5);
131        drawLines(g, 1);
132        g.translate(0, 10);
133        drawLines(g, 4);
134
135        // fillRect tests...
136        g.translate(0, 10);
137        fillRects(g);
138
139        // drawRect tests...
140        g.translate(0, 20);
141        drawRects(g);
142
143        // fillOval tests...
144        g.translate(0, 20);
145        fillOvals(g);
146
147        // drawOval tests...
148        g.translate(0, 20);
149        drawOvals(g);
150    }
151
152    private void renderTest(Graphics2D g, int w, int h) {
153        // on the left side, render the shapes in solid mode
154        g.setColor(Color.black);
155        g.fillRect(0, 0, w, h);
156        g.setColor(Color.green);
157        renderShapes(g);
158
159        // on the right side, render the shapes in XOR mode
160        g.setTransform(AffineTransform.getTranslateInstance(SIZE/2, 0));
161        g.setXORMode(Color.black);
162        renderShapes(g);
163        g.setTransform(AffineTransform.getTranslateInstance(SIZE/2, 0));
164        renderShapes(g);
165    }
166
167    public void paint(Graphics g) {
168
169        Graphics2D g2d = (Graphics2D)g;
170        renderTest(g2d, SIZE, SIZE);
171
172        Toolkit.getDefaultToolkit().sync();
173
174        synchronized (this) {
175            if (!done) {
176                doCapture(this);
177                done = true;
178            }
179            notifyAll();
180        }
181    }
182
183    public Dimension getPreferredSize() {
184        return new Dimension(SIZE, SIZE);
185    }
186
187    public static void main(String[] args) {
188        boolean show = false;
189        for (String arg : args) {
190            if (arg.equals("-testvi")) {
191                System.out.println("Testing VolatileImage, not screen");
192                testVI = true;
193            } else if (arg.equals("-show")) {
194                show = true;
195            }
196        }
197
198        SimplePrimQuality test = new SimplePrimQuality();
199        Frame frame = new Frame();
200        frame.add(test);
201        frame.pack();
202        frame.setVisible(true);
203
204        // Wait until the component's been painted
205        synchronized (test) {
206            while (!done) {
207                try {
208                    test.wait();
209                } catch (InterruptedException e) {
210                    throw new RuntimeException("Failed: Interrupted");
211                }
212            }
213        }
214
215        // REMIND: We will allow this test to pass silently on Windows
216        // (when OGL is not enabled) until we fix the GDI pipeline so that
217        // its stroked/filled GeneralPaths match our software loops (see
218        // 6322554).  This check should be removed when 6322554 is fixed.
219        GraphicsConfiguration gc = frame.getGraphicsConfiguration();
220        if (gc.getClass().getSimpleName().startsWith("Win")) {
221            System.out.println("GDI pipeline detected: " +
222                               "test considered PASSED");
223            frame.dispose();
224            return;
225        }
226
227
228        if (testVI) {
229            // render to a VI instead of the screen
230            VolatileImage vi = frame.createVolatileImage(SIZE, SIZE);
231            do {
232                vi.validate(frame.getGraphicsConfiguration());
233                Graphics2D g1 = vi.createGraphics();
234                test.renderTest(g1, SIZE, SIZE);
235                g1.dispose();
236                capture = vi.getSnapshot();
237            } while (vi.contentsLost());
238            frame.dispose();
239        }
240
241        if (!show) {
242            frame.dispose();
243        }
244        if (capture == null) {
245            throw new RuntimeException("Error capturing the rendering");
246        }
247
248        // Create reference image
249        int w = SIZE, h = SIZE;
250        BufferedImage refimg = new BufferedImage(w, h,
251                                                 BufferedImage.TYPE_INT_RGB);
252        Graphics2D g = refimg.createGraphics();
253        test.renderTest(g, w, h);
254        g.dispose();
255
256        // Test pixels
257        for (int y = 0; y < h; y++) {
258            for (int x = 0; x < w; x++) {
259                int actual = capture.getRGB(x, y);
260                int expected = refimg.getRGB(x, y);
261                if (actual != expected) {
262                    String expectedName = "SimplePrimQuality_expected.png";
263                    String actualName = "SimplePrimQuality_actual.png";
264                    try {
265                        System.out.println("Writing expected image to: "+
266                                           expectedName);
267                        ImageIO.write(refimg, "png", new File(expectedName));
268                        System.out.println("Writing actual image   to: "+
269                                           actualName);
270                        ImageIO.write(capture, "png", new File(actualName));
271                    } catch (IOException ex) {}
272                    throw new RuntimeException("Test failed at x="+x+" y="+y+
273                                               " (expected="+
274                                               Integer.toHexString(expected) +
275                                               " actual="+
276                                               Integer.toHexString(actual) +
277                                               ")");
278                }
279            }
280        }
281    }
282}
283