1 /* 2 * Copyright (c) 2017, 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.BasicStroke; 25 import java.awt.BorderLayout; 26 import java.awt.Color; 27 import java.awt.Dimension; 28 import java.awt.Graphics; 29 import java.awt.Graphics2D; 30 import java.awt.GraphicsConfiguration; 31 import java.awt.GraphicsDevice; 32 import java.awt.GraphicsEnvironment; 33 import java.awt.GridBagConstraints; 34 import java.awt.GridBagLayout; 35 import java.awt.Image; 36 import java.awt.Rectangle; 37 import java.awt.Robot; 38 import java.awt.event.ActionEvent; 39 import java.awt.event.ActionListener; 40 import java.awt.event.WindowAdapter; 41 import java.awt.event.WindowEvent; 42 import java.awt.geom.AffineTransform; 43 import java.awt.image.BufferedImage; 44 import java.awt.image.MultiResolutionImage; 45 import java.util.List; 46 import java.util.concurrent.CountDownLatch; 47 import java.util.concurrent.TimeUnit; 48 import javax.swing.JButton; 49 import javax.swing.JFrame; 50 import javax.swing.JPanel; 51 import javax.swing.JTextArea; 52 import javax.swing.SwingUtilities; 53 54 /* @test 55 * @bug 8173972 56 * @summary createScreenCapture not working as expected on multimonitor setup 57 * with different DPI scales. 58 * @run main/manual/othervm RobotMultiDPIScreenTest 59 */ 60 public class RobotMultiDPIScreenTest { 61 62 private static volatile boolean testResult = false; 63 private static volatile CountDownLatch countDownLatch; 64 private static JFrame mainFrame; 65 private static Rectangle maxBounds; 66 private static Rectangle[] screenBounds; 67 private static double[][] scales; 68 69 private static final String INSTRUCTIONS = "INSTRUCTIONS:\n" 70 + "Verify that screenshots are properly taken from monitors" 71 + " with different DPI.\n" 72 + "\n" 73 + "The test is applicable for a multi-monitor system where displays" 74 + " are configured to have different DPI\n" 75 + "\n" 76 + "1. Press Take Screenshots button\n" 77 + "Check that screenshots shown on the panel are properly taken.\n"; 78 79 public static void main(String args[]) throws Exception { 80 81 countDownLatch = new CountDownLatch(1); 82 SwingUtilities.invokeLater(RobotMultiDPIScreenTest::createUI); 83 countDownLatch.await(15, TimeUnit.MINUTES); 84 if (!testResult) { 85 throw new RuntimeException("Test fails!"); 86 } 87 } 88 89 private static void createUI() { 90 91 initScreenBounds(); 92 93 mainFrame = new JFrame("DPI change test"); 94 GridBagLayout layout = new GridBagLayout(); 95 JPanel mainControlPanel = new JPanel(layout); 96 JPanel resultButtonPanel = new JPanel(layout); 97 98 GridBagConstraints gbc = new GridBagConstraints(); 99 100 JPanel testPanel = new JPanel(new BorderLayout()); 101 102 final BufferedImage screensImage = getScreenImages(); 103 final JPanel screensPanel = new JPanel() { 104 105 @Override 106 public void paint(Graphics g) { 107 super.paint(g); 108 g.drawImage(screensImage, 0, 0, getWidth(), getHeight(), this); 109 } 110 }; 111 112 screensPanel.setPreferredSize(new Dimension(400, 200)); 113 114 JButton frameButton = new JButton("Take Screenshots"); 115 frameButton.addActionListener((e) -> { 116 117 try { 118 Robot robot = new Robot(); 119 Graphics2D g = screensImage.createGraphics(); 120 g.translate(-maxBounds.x, -maxBounds.y); 121 122 for (Rectangle rect : screenBounds) { 123 MultiResolutionImage mrImage = robot.createMultiResolutionScreenCapture(rect); 124 125 List<Image> resolutionVariants = mrImage.getResolutionVariants(); 126 Image rvImage = resolutionVariants.get(resolutionVariants.size() - 1); 127 g.drawImage(rvImage, rect.x, rect.y, rect.width, rect.height, null); 128 } 129 130 g.dispose(); 131 screensPanel.repaint(); 132 } catch (Exception ex) { 133 throw new RuntimeException(ex); 134 } 135 }); 136 137 testPanel.add(screensPanel, BorderLayout.CENTER); 138 testPanel.add(frameButton, BorderLayout.SOUTH); 139 140 gbc.gridx = 0; 141 gbc.gridy = 0; 142 gbc.fill = GridBagConstraints.HORIZONTAL; 143 mainControlPanel.add(testPanel, gbc); 144 145 JTextArea instructionTextArea = new JTextArea(); 146 instructionTextArea.setText(INSTRUCTIONS); 147 instructionTextArea.setEditable(false); 148 instructionTextArea.setBackground(Color.white); 149 150 gbc.gridx = 0; 151 gbc.gridy = 1; 152 gbc.fill = GridBagConstraints.HORIZONTAL; 153 mainControlPanel.add(instructionTextArea, gbc); 154 155 JButton passButton = new JButton("Pass"); 156 passButton.setActionCommand("Pass"); 157 passButton.addActionListener((ActionEvent e) -> { 158 testResult = true; 159 disposeFrames(); 160 countDownLatch.countDown(); 161 162 }); 163 164 JButton failButton = new JButton("Fail"); 165 failButton.setActionCommand("Fail"); 166 failButton.addActionListener(new ActionListener() { 167 @Override 168 public void actionPerformed(ActionEvent e) { 169 disposeFrames(); 170 countDownLatch.countDown(); 171 } 172 }); 173 174 gbc.gridx = 0; 175 gbc.gridy = 0; 176 resultButtonPanel.add(passButton, gbc); 177 178 gbc.gridx = 1; 179 gbc.gridy = 0; 180 resultButtonPanel.add(failButton, gbc); 181 182 gbc.gridx = 0; 183 gbc.gridy = 2; 184 mainControlPanel.add(resultButtonPanel, gbc); 185 186 mainFrame.add(mainControlPanel); 187 mainFrame.pack(); 188 189 mainFrame.addWindowListener(new WindowAdapter() { 190 191 @Override 192 public void windowClosing(WindowEvent e) { 193 disposeFrames(); 194 countDownLatch.countDown(); 195 } 196 }); 197 mainFrame.setVisible(true); 198 } 199 200 private static void disposeFrames() { 201 if (mainFrame != null && mainFrame.isVisible()) { 202 mainFrame.dispose(); 203 } 204 } 205 206 static void initScreenBounds() { 207 208 GraphicsDevice[] devices = GraphicsEnvironment 209 .getLocalGraphicsEnvironment() 210 .getScreenDevices(); 211 212 screenBounds = new Rectangle[devices.length]; 213 scales = new double[devices.length][2]; 214 for (int i = 0; i < devices.length; i++) { 215 GraphicsConfiguration gc = devices[i].getDefaultConfiguration(); 216 screenBounds[i] = gc.getBounds(); 217 AffineTransform tx = gc.getDefaultTransform(); 218 scales[i][0] = tx.getScaleX(); 219 scales[i][1] = tx.getScaleY(); 220 } 221 222 maxBounds = screenBounds[0]; 223 for (int i = 0; i < screenBounds.length; i++) { 224 maxBounds = maxBounds.union(screenBounds[i]); 225 } 226 } 227 228 private static Rectangle getCenterRect(Rectangle rect) { 229 int w = rect.width / 2; 230 int h = rect.height / 2; 231 int x = rect.x + w / 2; 232 int y = rect.y + h / 2; 233 234 return new Rectangle(x, y, w, h); 235 } 236 237 static BufferedImage getScreenImages() { 238 239 final BufferedImage img = new BufferedImage(maxBounds.width, maxBounds.height, BufferedImage.TYPE_INT_RGB); 240 Graphics2D g = img.createGraphics(); 241 g.setColor(Color.WHITE); 242 g.fillRect(0, 0, maxBounds.width, maxBounds.height); 243 g.translate(-maxBounds.x, -maxBounds.y); 244 245 g.setStroke(new BasicStroke(8f)); 246 for (int i = 0; i < screenBounds.length; i++) { 247 Rectangle r = screenBounds[i]; 248 g.setColor(Color.BLACK); 249 g.drawRect(r.x, r.y, r.width, r.height); 250 251 g.setColor(Color.ORANGE); 252 Rectangle cr = getCenterRect(r); 253 g.fillRect(cr.x, cr.y, cr.width, cr.height); 254 255 double scaleX = scales[i][0]; 256 double scaleY = scales[i][1]; 257 float fontSize = maxBounds.height / 7; 258 g.setFont(g.getFont().deriveFont(fontSize)); 259 g.setColor(Color.BLUE); 260 g.drawString(String.format("Scale: [%2.1f, %2.1f]", scaleX, scaleY), 261 r.x + r.width / 8, r.y + r.height / 2); 262 263 } 264 265 g.dispose(); 266 267 return img; 268 } 269 }