/* * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * @test * @bug 4925447 4887054 8285604 * @key headful * @summary verifies that most basic rendering operations going are successfully * clipped against a complex clip shape * @run main ClipShapeRendering */ /** * This test creates a complex clip shape (basically, a rectangular hole * cut out of the center of the rendering area) and then cycles through * various rendering primitives (image copies, lines, text, and shapes) * under various situations (default, scaled transform, and wide lines). * After all of that, Robot is used to check whether the clip shape has * been disturbed by any of the rendering (no pixels from the rendering * operations should have done anything inside the clipped-out area); the * test passes or fails based on whether the clip shape is undisturbed. * * There is a performance-test version of this app which runs all of the * tests in loops and times the results. This can be useful to see, for * example, the difference in performance between old and new internal * implementations of this clip-shape situation. To run the performance * test, run ClipShapeRendering -perf. */ import java.awt.AWTException; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Robot; import java.awt.Stroke; import java.awt.Toolkit; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DirectColorModel; import java.awt.image.WritableRaster; public class ClipShapeRendering extends Frame { BufferedImage image; BufferedImage imageBM; static Image offScreenImage; static boolean timeToRun = false; static Color imageColor = Color.red; static Color fillColor = Color.blue; static boolean perfMode = false; static boolean showCapture = false; static Rectangle clipRect = new Rectangle(100, 100, 100, 100); // move away from cursor private final static int OFFSET_X = 20; private final static int OFFSET_Y = 20; public ClipShapeRendering() { super("On-screen rendering test frame"); } public void initImages(Color imageColor) { int w = getWidth(); int h = getHeight(); ColorModel cm = new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000); WritableRaster wr = cm.createCompatibleWritableRaster(w, h); imageBM = new BufferedImage(cm, wr, cm.isAlphaPremultiplied(), null); Graphics g2 = imageBM.createGraphics(); g2.setColor(imageColor); g2.fillRect(0, 0, w, h); image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); g2 = image.createGraphics(); g2.setColor(imageColor); g2.fillRect(0,0, w, h); g2.dispose(); offScreenImage = createImage(w, h); } public void paint(Graphics g) { synchronized (this) { timeToRun = true; notifyAll(); } } public void runTests() { initImages(imageColor); // run off-screen test System.out.println("Running OFF-SCREEN tests.."); runTest(offScreenImage.getGraphics()); // run on-screen test System.out.println("Running ON-SCREEN tests.."); runTest(getGraphics()); } /** * Set various parameters on the Graphics object and call the * rendering loop with each variation */ public void runTest(Graphics g) { int w = getWidth(); int h = getHeight(); Area area = new Area( new Rectangle(0,0, w, h)); area.subtract(new Area(clipRect)); // Fill completely with background color g.setColor(fillColor); g.fillRect(0, 0, w, h); // Set the clip shape g.setClip(area); // Now perform various rendering operations g.setColor(Color.black); Graphics2D g2 = (Graphics2D)g; if (perfMode) { System.out.println("Default Graphics Results:"); System.out.println("-------------------------"); } renderingLoop(g2); if (perfMode) { System.out.println("Scaling Transform Results:"); System.out.println("-------------------------"); } AffineTransform oldXform = g2.getTransform(); g2.scale(.9, 1.15); renderingLoop(g2); g2.setTransform(oldXform); if (perfMode) { System.out.println("Wide Lines Results:"); System.out.println("-------------------"); } Stroke oldStroke = g2.getStroke(); g2.setStroke(new BasicStroke(5.0f)); renderingLoop(g2); g2.setStroke(oldStroke); } public void renderingLoop(Graphics2D g) { int numReps = 1; int numTextReps = 1; long start, end; if (perfMode) { numReps = 1000; numTextReps = 50; } int w = getWidth(); int h = getHeight(); // Image copies robot.waitForIdle(); start = System.currentTimeMillis(); for (int i = 0; i < numReps; ++i) { g.drawImage(image, 0, 0, null); g.drawImage(image, -10, -10, null); g.drawImage(image, 50, 50, null); g.drawImage(image, 40, 10, null); } for (int i = 0; i < numReps; ++i) { g.drawImage(imageBM, 0, 0, Color.yellow, null); g.drawImage(imageBM, -10, -10, Color.yellow, null); g.drawImage(imageBM, 50, 50, Color.yellow, null); g.drawImage(imageBM, 40, 10, Color.yellow, null); } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("Image Copies : " + (end - start) + " ms"); } // Image scales start = System.currentTimeMillis(); for (int i = 0; i < numReps; ++i) { g.drawImage(image, 0, 0, 500, 500, null); } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("Image scales : " + (end - start) + " ms"); } // Lines start = System.currentTimeMillis(); for (int i = 0; i < numReps; ++i) { g.drawLine(0, 0, w, h); g.drawLine(0, h, w, 0); g.drawLine(0, h / 2, w, h / 2); g.drawLine(w / 2, 0, w / 2, h); } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("drawLine : " + (end - start) + " ms"); } // Text // Non-AA start = System.currentTimeMillis(); for (int i = 0; i < numTextReps; ++i) { for (int x = 0; x < w; x += 20) { for (int y = 0; y < h; y += 17) { g.drawString("This is a string, this is only a string", x, y); } } } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("Text Non-AA : " + (end - start) + " ms"); } // Anti-Aliased Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); start = System.currentTimeMillis(); for (int i = 0; i < numTextReps; ++i) { for (int x = 0; x < w; x += 20) { for (int y = 0; y < h; y += 17) { g.drawString("This is a string, this is only a string", x, y); } } } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("Text General AA: " + (end - start) + " ms"); } g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); // Text AA g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); start = System.currentTimeMillis(); for (int i = 0; i < numTextReps; ++i) { for (int x = 0; x < w; x += 20) { for (int y = 0; y < h; y += 17) { g.drawString("This is a string, this is only a string", x, y); } } } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("Text textAA : " + (end - start) + " ms"); } g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); // Arcs start = System.currentTimeMillis(); if (numReps > 1) { numReps /= 10; } for (int i = 0; i < numReps; ++i) { for (int x = 0; x < w; x += 20) { for (int y = 0; y < h; y += 17) { g.drawArc(x, y, 30, 30, 0, 45); g.fillArc(x, y, 25, 25, 0, 45); } } } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("arcs : " + (end - start) + " ms"); } // Ovals start = System.currentTimeMillis(); if (numReps > 1) { numReps /= 10; } for (int i = 0; i < numReps; ++i) { for (int x = 0; x < w; x += 20) { for (int y = 0; y < h; y += 17) { g.drawOval(x, y, 20, 20); g.fillOval(x, y, 15, 15); } } } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("ovals : " + (end - start) + " ms"); } // Rects start = System.currentTimeMillis(); for (int i = 0; i < numReps; ++i) { for (int x = 0; x < w; x += 20) { for (int y = 0; y < h; y += 17) { g.drawRect(x, y, 20, 20); g.fillRect(x, y, 18, 18); } } } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("rects : " + (end - start) + " ms"); } // GeneralPath rendering GeneralPath drawGP = new GeneralPath(); for (int i = 0; i < 30; ++i) { Rectangle rect = new Rectangle(5 * i, 2 * i, 27, 19); drawGP.append(rect, false); } GeneralPath fillGP = new GeneralPath(); for (int i = 0; i < 30; ++i) { Rectangle rect = new Rectangle(5 * i, 100 + 2 * i, 27, 19); fillGP.append(rect, false); } Graphics2D g2d = (Graphics2D)g; start = System.currentTimeMillis(); for (int i = 0; i < numReps; ++i) { g2d.draw(drawGP); g2d.fill(fillGP); } robot.waitForIdle(); end = System.currentTimeMillis(); if (perfMode) { System.out.println("GeneralPath : " + (end - start) + " ms"); } } public static void usage() { System.err.println("java ClipShapeRendering [-perf] [-show]"); System.err.println(" -perf : runs performance benchmark (1000 reps)"); System.err.println(" -show : shows a frame with captured clip area"); System.exit(1); } public static boolean checkResult(BufferedImage clientPixels) { int pixels[] = new int[clipRect.width * clipRect.height]; clientPixels.getRGB(0, 0, clipRect.width, clipRect.height, pixels, 0, clipRect.width); int pixelIndex = 0; for (int row = 0; row < clipRect.height; ++row) { for (int col = 0; col < clipRect.width; ++col) { if (!(new Color(pixels[pixelIndex])).equals(fillColor)) { System.err.println("Incorrect color " + Integer.toHexString(pixels[pixelIndex]) + " instead of " + Integer.toHexString(fillColor.getRGB()) + " in pixel (" + (clipRect.x + col) + ", " + (clipRect.y + row) + ")"); return false; } pixelIndex++; } } return true; } static volatile Robot robot; public static void main(String args[]) throws Exception { for (int i = 0; i < args.length; ++i) { if (args[i].equals("-perf")) { perfMode = true; } else if (args[i].equals("-show")) { showCapture = true; } else { usage(); } } try { robot = new Robot(); } catch (AWTException e) { throw new RuntimeException("Can't create robot: " + e); } ClipShapeRendering clipTest = new ClipShapeRendering(); clipTest.setSize(300, 300); clipTest.setLocationRelativeTo(null); clipTest.setAlwaysOnTop(true); clipTest.setVisible(true); try { synchronized (clipTest) { while (!timeToRun) { clipTest.wait(300); } } } catch (InterruptedException e) {} clipTest.runTests(); // check off-screen rendering; BufferedImage offScreenClientPixels = (BufferedImage)clipTest.createImage(clipRect.width, clipRect.height); Graphics clientG = offScreenClientPixels.getGraphics(); clientG.drawImage(offScreenImage, 0, 0, clipRect.width, clipRect.height, clipRect.x, clipRect.y, clipRect.x + clipRect.width, clipRect.y + clipRect.height, null); if (showCapture) { CaptureFrame f = new CaptureFrame("OffScreen Image", offScreenImage); f.setVisible(true); } // check onscreen rendering Point clientLoc = clipTest.getLocationOnScreen(); Rectangle r = (Rectangle)clipRect.clone(); r.translate(clientLoc.x, clientLoc.y); // move mouse cursor away from captured region as in some system // cursor remain visible in composite captured image robot.mouseMove(r.x - OFFSET_X, r.y - OFFSET_Y); robot.waitForIdle(); BufferedImage onScreenClientPixels = robot.createScreenCapture(r); try { Thread.sleep(1000); } catch (Exception e) {} if (showCapture) { CaptureFrame f = new CaptureFrame("Onscreen clip area", onScreenClientPixels); f.setVisible(true); } else { clipTest.dispose(); } System.out.print("Checking results for off-screen rendering.."); boolean offScreenPassed = checkResult(offScreenClientPixels); System.out.println("done."); System.out.print("Checking results for on-screen rendering.."); boolean onScreenPassed = checkResult(onScreenClientPixels); System.out.println("done."); if (!offScreenPassed || !onScreenPassed) { javax.imageio.ImageIO.write(offScreenClientPixels, "png", new java.io.File("offscreen.png")); javax.imageio.ImageIO.write(onScreenClientPixels, "png", new java.io.File("onscreen.png")); throw new RuntimeException("Test failed. off-screen: " + (offScreenPassed?"passed":"failed") + " on-screen: " + (onScreenPassed?"passed":"failed")); } System.out.println("Passed"); } static { System.setProperty("sun.java2d.pmoffscreen", "true"); } } class CaptureFrame extends Frame { static int x = 300, y = 0; Image clientPixels; public CaptureFrame(String title, Image clientPixels) { super("Capture Frame: " + title); this.clientPixels = clientPixels; int w = clientPixels.getWidth(null); int h = clientPixels.getHeight(null); setSize(w, h + 30); setLocation(x, y); x += w; if (x + w > 1024) { x = 300; y += h; } add(new Component() { public void paint(Graphics g) { g.drawImage(CaptureFrame.this.clientPixels, 0, 0, null); } }); } };