1 /* 2 * Copyright (c) 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 import java.awt.Color; 25 import java.awt.FlowLayout; 26 import java.awt.Font; 27 import java.awt.Frame; 28 import java.awt.Graphics; 29 import java.awt.Graphics2D; 30 import java.awt.GridBagConstraints; 31 import java.awt.GridBagLayout; 32 import java.awt.HeadlessException; 33 import java.awt.Image; 34 import java.awt.Insets; 35 import java.awt.Panel; 36 import java.awt.event.ActionEvent; 37 import java.awt.event.ActionListener; 38 import java.awt.event.WindowAdapter; 39 import java.awt.event.WindowEvent; 40 import java.awt.geom.AffineTransform; 41 import java.awt.image.AbstractMultiResolutionImage; 42 import java.awt.image.BufferedImage; 43 import java.awt.image.ImageObserver; 44 import java.util.Arrays; 45 import java.util.Collections; 46 import java.util.List; 47 import java.util.concurrent.CountDownLatch; 48 import java.util.concurrent.TimeUnit; 49 import javax.swing.JButton; 50 import javax.swing.JFrame; 51 import javax.swing.JPanel; 52 import javax.swing.JTextArea; 53 import javax.swing.SwingUtilities; 54 55 /* @test 56 * @bug 8147440 8147016 57 * @summary HiDPI (Windows): Swing components have incorrect sizes after 58 * changing display resolution 59 * @author Alexander Scherbatiy 60 * @run main/manual/othervm WindowResizingOnDPIChangingTest 61 */ 62 public class WindowResizingOnDPIChangingTest { 63 64 private static volatile boolean testResult = false; 65 private static volatile CountDownLatch countDownLatch; 66 private static TestFrame undecoratedFrame; 67 private static TestFrame decoratedFrame; 68 private static JFrame mainFrame; 69 70 private static final String INSTRUCTIONS = "INSTRUCTIONS:\n" 71 + "Verify that window is properly resized after the display DPI updating.\n" 72 + "\n" 73 + "The test is applicable for OSes that allows to change the display DPI size\n" 74 + "without the system rebooting (like Windows 8.1 and higher). Press PASS for other\n" 75 + "systems. \n" 76 + "\n" 77 + "1. Set the display DPI size to 192 (DPI scale factor 200%)\n" 78 + "2. Press Show Frames button\n" 79 + "Two frames decorated and undecorated appear.\n" 80 + "3. Check that the string \"scale 2x\" is painted on the windows.\n" 81 + "4. Set the display DPI size to 96 (DPI scale factor 100%)\n" 82 + "5. Check that the string \"scale: 1x\" is painted on the windows.\n" 83 + "6. Check that the windows are properly resized in the same way as native applications\n" 84 + "7. Check that the windows are properly repainted and do not contain drawing artifacts\n" 85 + "If so, press PASS, else press FAIL.\n"; 86 87 public static void main(String args[]) throws Exception { 88 89 countDownLatch = new CountDownLatch(1); 90 SwingUtilities.invokeLater(WindowResizingOnDPIChangingTest::createUI); 91 countDownLatch.await(15, TimeUnit.MINUTES); 92 if (!testResult) { 93 throw new RuntimeException("Test fails!"); 94 } 95 } 96 97 private static void createUI() { 98 99 mainFrame = new JFrame("DPI change test"); 100 GridBagLayout layout = new GridBagLayout(); 101 JPanel mainControlPanel = new JPanel(layout); 102 JPanel resultButtonPanel = new JPanel(layout); 103 104 GridBagConstraints gbc = new GridBagConstraints(); 105 106 JPanel testPanel = new JPanel(new FlowLayout()); 107 JButton frameButton = new JButton("Show Frames"); 108 frameButton.addActionListener((e) -> { 109 int x = 20; 110 int y = 10; 111 int w = 400; 112 int h = 300; 113 114 undecoratedFrame = new TestFrame(w, h, true); 115 undecoratedFrame.setLocation(x, y); 116 undecoratedFrame.setVisible(true); 117 118 decoratedFrame = new TestFrame(w, h, false); 119 decoratedFrame.setLocation(x + w + 10, y); 120 decoratedFrame.setVisible(true); 121 122 }); 123 testPanel.add(frameButton); 124 125 gbc.gridx = 0; 126 gbc.gridy = 0; 127 gbc.fill = GridBagConstraints.HORIZONTAL; 128 mainControlPanel.add(testPanel, gbc); 129 130 JTextArea instructionTextArea = new JTextArea(); 131 instructionTextArea.setText(INSTRUCTIONS); 132 instructionTextArea.setEditable(false); 133 instructionTextArea.setBackground(Color.white); 134 135 gbc.gridx = 0; 136 gbc.gridy = 1; 137 gbc.fill = GridBagConstraints.HORIZONTAL; 138 mainControlPanel.add(instructionTextArea, gbc); 139 140 JButton passButton = new JButton("Pass"); 141 passButton.setActionCommand("Pass"); 142 passButton.addActionListener((ActionEvent e) -> { 143 testResult = true; 144 disposeFrames(); 145 countDownLatch.countDown(); 146 147 }); 148 149 JButton failButton = new JButton("Fail"); 150 failButton.setActionCommand("Fail"); 151 failButton.addActionListener(new ActionListener() { 152 @Override 153 public void actionPerformed(ActionEvent e) { 154 disposeFrames(); 155 countDownLatch.countDown(); 156 } 157 }); 158 159 gbc.gridx = 0; 160 gbc.gridy = 0; 161 resultButtonPanel.add(passButton, gbc); 162 163 gbc.gridx = 1; 164 gbc.gridy = 0; 165 resultButtonPanel.add(failButton, gbc); 166 167 gbc.gridx = 0; 168 gbc.gridy = 2; 169 mainControlPanel.add(resultButtonPanel, gbc); 170 171 mainFrame.add(mainControlPanel); 172 mainFrame.pack(); 173 174 mainFrame.addWindowListener(new WindowAdapter() { 175 176 @Override 177 public void windowClosing(WindowEvent e) { 178 disposeFrames(); 179 countDownLatch.countDown(); 180 } 181 }); 182 mainFrame.setVisible(true); 183 } 184 185 private static void disposeFrames() { 186 if (decoratedFrame != null && decoratedFrame.isVisible()) { 187 decoratedFrame.dispose(); 188 } 189 if (undecoratedFrame != null && undecoratedFrame.isVisible()) { 190 undecoratedFrame.dispose(); 191 } 192 if (mainFrame != null && mainFrame.isVisible()) { 193 mainFrame.dispose(); 194 } 195 } 196 197 static class TestFrame extends Frame { 198 199 private final TestMultiResolutionImage mrImage; 200 201 public TestFrame(int width, int height, boolean undecorated) throws HeadlessException { 202 super("Test Frame. Undecorated: " + undecorated); 203 setSize(width, height); 204 mrImage = new TestMultiResolutionImage(width, height); 205 206 setUndecorated(undecorated); 207 Panel panel = new Panel(new FlowLayout()) { 208 @Override 209 public void paint(Graphics g) { 210 super.paint(g); 211 AffineTransform tx = ((Graphics2D) g).getTransform(); 212 mrImage.scaleX = tx.getScaleX(); 213 mrImage.scaleY = tx.getScaleY(); 214 Insets insets = getInsets(); 215 g.drawImage(mrImage, insets.left, insets.bottom, null); 216 } 217 }; 218 add(panel); 219 } 220 } 221 222 static class TestMultiResolutionImage extends AbstractMultiResolutionImage { 223 224 final int width; 225 final int height; 226 double scaleX; 227 double scaleY; 228 229 public TestMultiResolutionImage(int width, int height) { 230 this.width = width; 231 this.height = height; 232 } 233 234 @Override 235 public int getWidth(ImageObserver observer) { 236 return width; 237 } 238 239 @Override 240 public int getHeight(ImageObserver observer) { 241 return height; 242 } 243 244 @Override 245 protected Image getBaseImage() { 246 return getResolutionVariant(width, height); 247 } 248 249 @Override 250 public Image getResolutionVariant(double destImageWidth, double destImageHeight) { 251 252 int w = (int) destImageWidth; 253 int h = (int) destImageHeight; 254 255 BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 256 Graphics2D g = img.createGraphics(); 257 g.scale(scaleX, scaleY); 258 int red = (int) (255 / scaleX); 259 int green = (int) (250 / scaleX); 260 int blue = (int) (20 / scaleX); 261 g.setColor(new Color(red, green, blue)); 262 g.fillRect(0, 0, width, height); 263 264 g.setColor(Color.decode("#87CEFA")); 265 Font f = g.getFont(); 266 g.setFont(new Font(f.getName(), Font.BOLD, 24)); 267 g.drawString(String.format("scales: [%1.2fx, %1.2fx]", scaleX, scaleY), 268 width / 6, height / 2); 269 270 g.dispose(); 271 return img; 272 } 273 274 @Override 275 public List<Image> getResolutionVariants() { 276 return Collections.unmodifiableList(Arrays.asList(getBaseImage())); 277 } 278 } 279 }