1 /* 2 * Copyright (c) 2003, 2019, 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 import java.awt.Color; 25 import java.awt.Component; 26 import java.awt.DisplayMode; 27 import java.awt.Frame; 28 import java.awt.Graphics; 29 import java.awt.GraphicsConfiguration; 30 import java.awt.GraphicsDevice; 31 import java.awt.GraphicsEnvironment; 32 import java.awt.Image; 33 import java.awt.Rectangle; 34 import java.awt.Robot; 35 import java.awt.Transparency; 36 import java.awt.Window; 37 import java.awt.event.KeyEvent; 38 import java.awt.event.KeyListener; 39 import java.awt.image.BufferedImage; 40 import java.util.ArrayList; 41 42 /** 43 * @test 44 * @bug 4836241 6364134 8232200 45 * @key headful 46 * @summary verify that images are restored correctly after display mode 47 * switches and that no other rendering or crash problems occur 48 * @run main/timeout=500 CycleDMImage 49 */ 50 public class CycleDMImage extends Component implements Runnable, KeyListener { 51 /** 52 * This test cycles through all available display modes, waiting after 53 * each call to setDisplayMode() to ensure that the new one is active 54 * before proceeding on to the next one. The Component is filled with 55 * a green background color and then compatible images of all 3 56 * Transparency types are copied to the screen. The results of these 57 * operations are checked (using Robot) and the test fails if any of the 58 * rendering is wrong in any of the DisplayModes. The purpose of this 59 * test is to ensure that display mode switches do not cause problems 60 * with image restoration (or other rendering operations). 61 */ 62 boolean painted = false; 63 boolean earlyExit = false; 64 Image rImage = null, wImage = null, bImage = null; 65 int imgSize = 10; 66 Robot robot = null; 67 volatile static boolean done = false; 68 static String errorMessage = null; 69 70 public synchronized void paint(Graphics g) { 71 if (!painted) { 72 painted = true; 73 (new Thread(this)).start(); 74 } 75 if (rImage == null) { 76 GraphicsConfiguration gc = getGraphicsConfiguration(); 77 rImage = gc.createCompatibleImage(imgSize, imgSize); 78 wImage = gc.createCompatibleImage(imgSize, imgSize, 79 Transparency.BITMASK); 80 bImage = gc.createCompatibleImage(imgSize, imgSize, 81 Transparency.TRANSLUCENT); 82 Graphics imgGraphics = rImage.getGraphics(); 83 imgGraphics.setColor(Color.red); 84 imgGraphics.fillRect(0, 0, imgSize, imgSize); 85 imgGraphics = wImage.getGraphics(); 86 imgGraphics.setColor(Color.white); 87 imgGraphics.fillRect(0, 0, imgSize, imgSize); 88 imgGraphics = bImage.getGraphics(); 89 imgGraphics.setColor(Color.blue); 90 imgGraphics.fillRect(0, 0, imgSize, imgSize); 91 } 92 g.setColor(Color.green); 93 g.fillRect(0, 0, getWidth(), getHeight()); 94 g.drawImage(rImage, 0, 0, this); 95 g.drawImage(wImage, imgSize, 0, this); 96 g.drawImage(bImage, imgSize*2, 0, this); 97 g.drawImage(rImage, 0, getHeight()-imgSize, null); 98 g.drawImage(rImage, getWidth()-imgSize, getHeight()-imgSize, null); 99 g.drawImage(rImage, getWidth()-imgSize, 0, null); 100 } 101 102 static void delay(long ms) { 103 try { 104 Thread.sleep(ms); 105 } catch (Exception e) {} 106 } 107 108 public boolean checkResult(DisplayMode dm) { 109 if (robot == null) { 110 try { 111 robot = new Robot(); 112 } 113 catch (Exception e) { 114 errorMessage = "Problems creating Robot"; 115 return false; 116 } 117 } 118 Rectangle bounds = getGraphicsConfiguration().getBounds(); 119 int pixels[] = new int[imgSize * 4]; 120 BufferedImage clientPixels = 121 robot.createScreenCapture(new Rectangle(bounds.x, bounds.y, 122 imgSize*4, 1)); 123 clientPixels.getRGB(0, 0, imgSize * 4, 1, pixels, 0, getWidth()); 124 // Now check the results. We expect: imgSize blocks of r/w/b/g 125 int colors[] = {0xffff0000, 0xffffffff, 0xff0000ff, 0xff00ff00}; 126 for (int color = 0; color < 4; ++color) { 127 for (int i = 0; i < imgSize; ++i) { 128 int pixelIndex = imgSize * color + i; 129 if (pixels[pixelIndex] != colors[color]) { 130 errorMessage = "\n DisplayMode(" + 131 dm.getWidth() + " x " + 132 dm.getHeight() + " x " + 133 dm.getBitDepth() + "bpp x " + 134 dm.getRefreshRate() + 135 ")\n Pixel " + i + 136 ": Expected " + 137 Integer.toHexString(colors[color]) + 138 ", got " + 139 Integer.toHexString(pixels[pixelIndex]) + 140 " at " + i; 141 return false; 142 } 143 } 144 } 145 return true; 146 } 147 148 boolean displayModesEqual(DisplayMode dm1, DisplayMode dm2) { 149 if (dm1.equals(dm2)) { 150 return true; 151 } 152 // not enough - check whether the modes are equal except for 153 // refreshRate, if either mode has REFRESH_RATE_UNKNOWN 154 // value for this parameter 155 if (dm1.getWidth() != dm2.getWidth() || 156 dm1.getHeight() != dm2.getHeight() || 157 dm1.getBitDepth() != dm2.getBitDepth()) 158 { 159 // core parameters must match 160 return false; 161 } 162 // Now we know that w1 == w2, h1 == h2, and d1 == d2; must be the 163 // case that the refresh rates do not match. 164 // Is either one REFRESH_RATE_UNKNOWN? 165 if (dm1.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN || 166 dm2.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN) 167 { 168 return true; 169 } 170 return false; 171 } 172 173 public void run() { 174 GraphicsDevice gd = getGraphicsConfiguration().getDevice(); 175 gd.setFullScreenWindow((Window) getParent()); 176 // First, delay a bit just to let the fullscreen window 177 // settle down before switching display modes 178 delay(1000); 179 180 if (!gd.isDisplayChangeSupported()) { 181 System.err.println("Display change is not supported,"+ 182 " the test is considered passed."); 183 finished(); 184 return; 185 } 186 187 // We are really only interested in unique w/h/d resolutions 188 // and it would be nice to skip the myriad of refresh rate 189 // varations, so let us construct a subset that contains 190 // only those DisplayModes with unique w/h/d values 191 // Also, due to a current bug (4837228), we should skip the 192 // 24-bit depths since attempting to create bitmask-transparent 193 // ddraw images can cause the test to crash (we should change this 194 // test to include that depth when the bug is fixed). 195 ArrayList<DisplayMode> dmSubset = new ArrayList<>(); 196 for (final DisplayMode dm : gd.getDisplayModes()) { 197 boolean skip = false; 198 for (final DisplayMode dmUnique : dmSubset) { 199 int bitDepth = dm.getBitDepth(); 200 if (bitDepth == 24 || 201 (dmUnique.getWidth() == dm.getWidth() && 202 dmUnique.getHeight() == dm.getHeight() && 203 dmUnique.getBitDepth() == dm.getBitDepth())) { 204 skip = true; 205 break; 206 } 207 } 208 if (!skip) { 209 dmSubset.add(dm); 210 } 211 } 212 213 // Now, cycle through the display modes one-by-one. For 214 // each new display mode, delay until we detect that the 215 // new mode == the current mode. Then delay an additional 216 // second (to allow any repaints to occur) 217 218 for (DisplayMode newDM : dmSubset) { 219 gd.setDisplayMode(newDM); 220 while (!displayModesEqual(newDM, gd.getDisplayMode())) { 221 delay(100); 222 } 223 // Delay another few seconds after the new display mode is active 224 delay(4000); 225 226 // Check the rendering results 227 if (!checkResult(newDM)) { 228 finished(); 229 return; 230 } 231 232 // Escape out if requested by the user 233 if (earlyExit) { 234 System.out.println("Exiting test early, by request"); 235 System.exit(0); 236 } 237 } 238 239 // Done with test; if we got here, we passed 240 System.out.println("Passed"); 241 finished(); 242 } 243 244 public static void finished() { 245 synchronized (CycleDMImage.class) { 246 done = true; 247 CycleDMImage.class.notify(); 248 } 249 } 250 251 /** 252 * KeyListener methods; these provide a way for a user to escape out of 253 * a potentially lengthy test. 254 */ 255 256 public void keyTyped(KeyEvent e) { 257 } 258 259 public void keyPressed(KeyEvent e) { 260 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { 261 earlyExit = true; 262 } 263 } 264 265 public void keyReleased(KeyEvent e) { 266 } 267 268 public static void main(String args[]) { 269 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 270 for (final GraphicsDevice gd: ge.getScreenDevices()) { 271 if (!gd.isFullScreenSupported()) { 272 System.err.println("FullScreen mode is not supported,"+ 273 " the test is considered passed."); 274 continue; 275 } 276 done = false; 277 Frame frame = new Frame(gd.getDefaultConfiguration()); 278 try { 279 frame.setSize(400, 400); 280 frame.setUndecorated(true); 281 CycleDMImage comp = new CycleDMImage(); 282 frame.addKeyListener(comp); 283 frame.add(comp); 284 frame.setVisible(true); 285 // Sleep awaiting frame disposal 286 synchronized (CycleDMImage.class) { 287 while (!done) { 288 try { 289 CycleDMImage.class.wait(100); 290 } catch (InterruptedException e) { 291 } 292 } 293 } 294 } finally { 295 frame.dispose(); 296 } 297 if (errorMessage != null) { 298 throw new RuntimeException(errorMessage); 299 } 300 // delay a bit just to let the fullscreen window disposing complete 301 // before switching to next display 302 delay(4000); 303 } 304 } 305 }