/* * Copyright (c) 2003, 2020, 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. */ import java.awt.Color; import java.awt.Component; import java.awt.DisplayMode; import java.awt.Frame; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Rectangle; import java.awt.Robot; import java.awt.Transparency; import java.awt.Window; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import java.util.ArrayList; /** * @test * @bug 4836241 6364134 8232200 * @key headful * @summary verify that images are restored correctly after display mode * switches and that no other rendering or crash problems occur * @run main/timeout=500 CycleDMImage */ public class CycleDMImage extends Component implements Runnable, KeyListener { /** * This test cycles through all available display modes, waiting after * each call to setDisplayMode() to ensure that the new one is active * before proceeding on to the next one. The Component is filled with * a green background color and then compatible images of all 3 * Transparency types are copied to the screen. The results of these * operations are checked (using Robot) and the test fails if any of the * rendering is wrong in any of the DisplayModes. The purpose of this * test is to ensure that display mode switches do not cause problems * with image restoration (or other rendering operations). */ boolean painted = false; boolean earlyExit = false; Image rImage = null, wImage = null, bImage = null; int imgSize = 10; static Robot robot = null; volatile static boolean done = false; static String errorMessage = null; public synchronized void paint(Graphics g) { if (!painted) { painted = true; (new Thread(this)).start(); } if (rImage == null) { GraphicsConfiguration gc = getGraphicsConfiguration(); rImage = gc.createCompatibleImage(imgSize, imgSize); wImage = gc.createCompatibleImage(imgSize, imgSize, Transparency.BITMASK); bImage = gc.createCompatibleImage(imgSize, imgSize, Transparency.TRANSLUCENT); Graphics imgGraphics = rImage.getGraphics(); imgGraphics.setColor(Color.red); imgGraphics.fillRect(0, 0, imgSize, imgSize); imgGraphics = wImage.getGraphics(); imgGraphics.setColor(Color.white); imgGraphics.fillRect(0, 0, imgSize, imgSize); imgGraphics = bImage.getGraphics(); imgGraphics.setColor(Color.blue); imgGraphics.fillRect(0, 0, imgSize, imgSize); } g.setColor(Color.green); g.fillRect(0, 0, getWidth(), getHeight()); g.drawImage(rImage, 0, 0, this); g.drawImage(wImage, imgSize, 0, this); g.drawImage(bImage, imgSize*2, 0, this); g.drawImage(rImage, 0, getHeight()-imgSize, null); g.drawImage(rImage, getWidth()-imgSize, getHeight()-imgSize, null); g.drawImage(rImage, getWidth()-imgSize, 0, null); } static void delay(long ms) { try { Thread.sleep(ms); } catch (Exception e) {} } public boolean checkResult(DisplayMode dm) { Rectangle bounds = getGraphicsConfiguration().getBounds(); int pixels[] = new int[imgSize * 4]; BufferedImage clientPixels = robot.createScreenCapture(new Rectangle(bounds.x, bounds.y, imgSize*4, 1)); clientPixels.getRGB(0, 0, imgSize * 4, 1, pixels, 0, getWidth()); // Now check the results. We expect: imgSize blocks of r/w/b/g int colors[] = {0xffff0000, 0xffffffff, 0xff0000ff, 0xff00ff00}; for (int color = 0; color < 4; ++color) { for (int i = 0; i < imgSize; ++i) { int pixelIndex = imgSize * color + i; if (pixels[pixelIndex] != colors[color]) { errorMessage = "\n DisplayMode(" + dm.getWidth() + " x " + dm.getHeight() + " x " + dm.getBitDepth() + "bpp x " + dm.getRefreshRate() + ")\n Pixel " + i + ": Expected " + Integer.toHexString(colors[color]) + ", got " + Integer.toHexString(pixels[pixelIndex]) + " at " + i; return false; } } } return true; } boolean displayModesEqual(DisplayMode dm1, DisplayMode dm2) { if (dm1.equals(dm2)) { return true; } // not enough - check whether the modes are equal except for // refreshRate, if either mode has REFRESH_RATE_UNKNOWN // value for this parameter if (dm1.getWidth() != dm2.getWidth() || dm1.getHeight() != dm2.getHeight() || dm1.getBitDepth() != dm2.getBitDepth()) { // core parameters must match return false; } // Now we know that w1 == w2, h1 == h2, and d1 == d2; must be the // case that the refresh rates do not match. // Is either one REFRESH_RATE_UNKNOWN? if (dm1.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN || dm2.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN) { return true; } return false; } public void run() { GraphicsDevice gd = getGraphicsConfiguration().getDevice(); gd.setFullScreenWindow((Window) getParent()); // First, delay a bit just to let the fullscreen window // settle down before switching display modes robot.waitForIdle(); delay(1000); if (!gd.isDisplayChangeSupported()) { System.err.println("Display change is not supported,"+ " the test is considered passed."); finished(); return; } // We are really only interested in unique w/h/d resolutions // and it would be nice to skip the myriad of refresh rate // varations, so let us construct a subset that contains // only those DisplayModes with unique w/h/d values // Also, due to a current bug (4837228), we should skip the // 24-bit depths since attempting to create bitmask-transparent // ddraw images can cause the test to crash (we should change this // test to include that depth when the bug is fixed). ArrayList dmSubset = new ArrayList<>(); for (final DisplayMode dm : gd.getDisplayModes()) { boolean skip = false; for (final DisplayMode dmUnique : dmSubset) { int bitDepth = dm.getBitDepth(); if (bitDepth == 24 || (dmUnique.getWidth() == dm.getWidth() && dmUnique.getHeight() == dm.getHeight() && dmUnique.getBitDepth() == dm.getBitDepth())) { skip = true; break; } } if (!skip) { dmSubset.add(dm); } } // Now, cycle through the display modes one-by-one. For // each new display mode, delay until we detect that the // new mode == the current mode. Then delay an additional // second (to allow any repaints to occur) for (DisplayMode newDM : dmSubset) { gd.setDisplayMode(newDM); while (!displayModesEqual(newDM, gd.getDisplayMode())) { delay(100); } // Delay another few seconds after the new display mode is active delay(4000); // Check the rendering results if (!checkResult(newDM)) { finished(); return; } // Escape out if requested by the user if (earlyExit) { System.out.println("Exiting test early, by request"); System.exit(0); } } // Done with test; if we got here, we passed System.out.println("Passed"); finished(); } public static void finished() { synchronized (CycleDMImage.class) { done = true; CycleDMImage.class.notify(); } } /** * KeyListener methods; these provide a way for a user to escape out of * a potentially lengthy test. */ public void keyTyped(KeyEvent e) { } public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { earlyExit = true; } } public void keyReleased(KeyEvent e) { } public static void main(String args[]) throws Exception { robot = new Robot(); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); for (final GraphicsDevice gd: ge.getScreenDevices()) { if (!gd.isFullScreenSupported()) { System.err.println("FullScreen mode is not supported,"+ " the test is considered passed."); continue; } done = false; Frame frame = new Frame(gd.getDefaultConfiguration()); try { frame.setSize(400, 400); frame.setUndecorated(true); CycleDMImage comp = new CycleDMImage(); frame.addKeyListener(comp); frame.add(comp); frame.setVisible(true); // Sleep awaiting frame disposal synchronized (CycleDMImage.class) { while (!done) { try { CycleDMImage.class.wait(100); } catch (InterruptedException e) { } } } } finally { frame.dispose(); } if (errorMessage != null) { throw new RuntimeException(errorMessage); } // delay a bit just to let the fullscreen window disposing complete // before switching to next display delay(4000); } } }