1 /* 2 * Copyright (c) 1997, 2020, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.java2d; 27 28 import java.awt.AWTError; 29 import java.awt.Color; 30 import java.awt.Dimension; 31 import java.awt.Font; 32 import java.awt.Graphics2D; 33 import java.awt.GraphicsConfiguration; 34 import java.awt.GraphicsDevice; 35 import java.awt.GraphicsEnvironment; 36 import java.awt.Insets; 37 import java.awt.Point; 38 import java.awt.Rectangle; 39 import java.awt.Toolkit; 40 import java.awt.geom.AffineTransform; 41 import java.awt.image.BufferedImage; 42 import java.awt.peer.ComponentPeer; 43 import java.security.AccessController; 44 import java.util.Locale; 45 import java.util.TreeMap; 46 47 import sun.awt.DisplayChangedListener; 48 import sun.awt.SunDisplayChanger; 49 import sun.font.FontManager; 50 import sun.font.FontManagerFactory; 51 import sun.font.FontManagerForSGE; 52 import sun.java2d.pipe.Region; 53 import sun.security.action.GetPropertyAction; 54 55 /** 56 * This is an implementation of a GraphicsEnvironment object for the 57 * default local GraphicsEnvironment. 58 * 59 * @see GraphicsDevice 60 * @see GraphicsConfiguration 61 */ 62 public abstract class SunGraphicsEnvironment extends GraphicsEnvironment 63 implements DisplayChangedListener { 64 65 /** Establish the default font to be used by SG2D. */ 66 private final Font defaultFont = new Font(Font.DIALOG, Font.PLAIN, 12); 67 68 private static final boolean uiScaleEnabled; 69 private static final double debugScale; 70 71 static { 72 uiScaleEnabled = "true".equals(AccessController.doPrivileged( 73 new GetPropertyAction("sun.java2d.uiScale.enabled", "true"))); 74 debugScale = uiScaleEnabled ? getScaleFactor("sun.java2d.uiScale") : -1; 75 } 76 77 protected GraphicsDevice[] screens; 78 79 /** 80 * Returns an array of all of the screen devices. 81 */ getScreenDevices()82 public synchronized GraphicsDevice[] getScreenDevices() { 83 GraphicsDevice[] ret = screens; 84 if (ret == null) { 85 int num = getNumScreens(); 86 ret = new GraphicsDevice[num]; 87 for (int i = 0; i < num; i++) { 88 ret[i] = makeScreenDevice(i); 89 } 90 screens = ret; 91 } 92 return ret; 93 } 94 95 /** 96 * Returns the number of screen devices of this graphics environment. 97 * 98 * @return the number of screen devices of this graphics environment 99 */ getNumScreens()100 protected abstract int getNumScreens(); 101 102 /** 103 * Create and return the screen device with the specified number. The 104 * device with number {@code 0} will be the default device (returned 105 * by {@link #getDefaultScreenDevice()}. 106 * 107 * @param screennum the number of the screen to create 108 * 109 * @return the created screen device 110 */ makeScreenDevice(int screennum)111 protected abstract GraphicsDevice makeScreenDevice(int screennum); 112 113 /** 114 * Returns the default screen graphics device. 115 */ getDefaultScreenDevice()116 public GraphicsDevice getDefaultScreenDevice() { 117 GraphicsDevice[] screens = getScreenDevices(); 118 if (screens.length == 0) { 119 throw new AWTError("no screen devices"); 120 } 121 return screens[0]; 122 } 123 124 /** 125 * Returns a Graphics2D object for rendering into the 126 * given BufferedImage. 127 * @throws NullPointerException if BufferedImage argument is null 128 */ createGraphics(BufferedImage img)129 public Graphics2D createGraphics(BufferedImage img) { 130 if (img == null) { 131 throw new NullPointerException("BufferedImage cannot be null"); 132 } 133 SurfaceData sd = SurfaceData.getPrimarySurfaceData(img); 134 return new SunGraphics2D(sd, Color.white, Color.black, defaultFont); 135 } 136 getFontManagerForSGE()137 public static FontManagerForSGE getFontManagerForSGE() { 138 FontManager fm = FontManagerFactory.getInstance(); 139 return (FontManagerForSGE) fm; 140 } 141 142 /* Modifies the behaviour of a subsequent call to preferLocaleFonts() 143 * to use Mincho instead of Gothic for dialoginput in JA locales 144 * on windows. Not needed on other platforms. 145 * 146 * @deprecated as of JDK9. To be removed in a future release 147 */ 148 @Deprecated useAlternateFontforJALocales()149 public static void useAlternateFontforJALocales() { 150 getFontManagerForSGE().useAlternateFontforJALocales(); 151 } 152 153 /** 154 * Returns all fonts available in this environment. 155 */ getAllFonts()156 public Font[] getAllFonts() { 157 FontManagerForSGE fm = getFontManagerForSGE(); 158 Font[] installedFonts = fm.getAllInstalledFonts(); 159 Font[] created = fm.getCreatedFonts(); 160 if (created == null || created.length == 0) { 161 return installedFonts; 162 } else { 163 int newlen = installedFonts.length + created.length; 164 Font [] fonts = java.util.Arrays.copyOf(installedFonts, newlen); 165 System.arraycopy(created, 0, fonts, 166 installedFonts.length, created.length); 167 return fonts; 168 } 169 } 170 getAvailableFontFamilyNames(Locale requestedLocale)171 public String[] getAvailableFontFamilyNames(Locale requestedLocale) { 172 FontManagerForSGE fm = getFontManagerForSGE(); 173 String[] installed = fm.getInstalledFontFamilyNames(requestedLocale); 174 /* Use a new TreeMap as used in getInstalledFontFamilyNames 175 * and insert all the keys in lower case, so that the sort order 176 * is the same as the installed families. This preserves historical 177 * behaviour and inserts new families in the right place. 178 * It would have been marginally more efficient to directly obtain 179 * the tree map and just insert new entries, but not so much as 180 * to justify the extra internal interface. 181 */ 182 TreeMap<String, String> map = fm.getCreatedFontFamilyNames(); 183 if (map == null || map.size() == 0) { 184 return installed; 185 } else { 186 for (int i=0; i<installed.length; i++) { 187 map.put(installed[i].toLowerCase(requestedLocale), 188 installed[i]); 189 } 190 String[] retval = new String[map.size()]; 191 Object [] keyNames = map.keySet().toArray(); 192 for (int i=0; i < keyNames.length; i++) { 193 retval[i] = map.get(keyNames[i]); 194 } 195 return retval; 196 } 197 } 198 getAvailableFontFamilyNames()199 public String[] getAvailableFontFamilyNames() { 200 return getAvailableFontFamilyNames(Locale.getDefault()); 201 } 202 203 /** 204 * Return the bounds of a GraphicsDevice, less its screen insets. 205 * See also java.awt.GraphicsEnvironment.getUsableBounds(); 206 */ getUsableBounds(GraphicsDevice gd)207 public static Rectangle getUsableBounds(GraphicsDevice gd) { 208 GraphicsConfiguration gc = gd.getDefaultConfiguration(); 209 Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc); 210 Rectangle usableBounds = gc.getBounds(); 211 212 usableBounds.x += insets.left; 213 usableBounds.y += insets.top; 214 usableBounds.width -= (insets.left + insets.right); 215 usableBounds.height -= (insets.top + insets.bottom); 216 217 return usableBounds; 218 } 219 220 /** 221 * From the DisplayChangedListener interface; called 222 * when the display mode has been changed. 223 */ displayChanged()224 public void displayChanged() { 225 // notify screens in device array to do display update stuff 226 for (GraphicsDevice gd : getScreenDevices()) { 227 if (gd instanceof DisplayChangedListener) { 228 ((DisplayChangedListener) gd).displayChanged(); 229 } 230 } 231 232 // notify SunDisplayChanger list (e.g. VolatileSurfaceManagers and 233 // SurfaceDataProxies) about the display change event 234 displayChanger.notifyListeners(); 235 } 236 237 /** 238 * Part of the DisplayChangedListener interface: 239 * propagate this event to listeners 240 */ paletteChanged()241 public void paletteChanged() { 242 displayChanger.notifyPaletteChanged(); 243 } 244 245 /** 246 * Returns true when the display is local, false for remote displays. 247 * 248 * @return true when the display is local, false for remote displays 249 */ isDisplayLocal()250 public abstract boolean isDisplayLocal(); 251 252 /* 253 * ----DISPLAY CHANGE SUPPORT---- 254 */ 255 256 protected SunDisplayChanger displayChanger = new SunDisplayChanger(); 257 258 /** 259 * Add a DisplayChangeListener to be notified when the display settings 260 * are changed. 261 */ addDisplayChangedListener(DisplayChangedListener client)262 public void addDisplayChangedListener(DisplayChangedListener client) { 263 displayChanger.add(client); 264 } 265 266 /** 267 * Remove a DisplayChangeListener from Win32GraphicsEnvironment 268 */ removeDisplayChangedListener(DisplayChangedListener client)269 public void removeDisplayChangedListener(DisplayChangedListener client) { 270 displayChanger.remove(client); 271 } 272 273 /* 274 * ----END DISPLAY CHANGE SUPPORT---- 275 */ 276 277 /** 278 * Returns true if FlipBufferStrategy with COPIED buffer contents 279 * is preferred for this peer's GraphicsConfiguration over 280 * BlitBufferStrategy, false otherwise. 281 * 282 * The reason FlipBS could be preferred is that in some configurations 283 * an accelerated copy to the screen is supported (like Direct3D 9) 284 * 285 * @return true if flip strategy should be used, false otherwise 286 */ isFlipStrategyPreferred(ComponentPeer peer)287 public boolean isFlipStrategyPreferred(ComponentPeer peer) { 288 return false; 289 } 290 isUIScaleEnabled()291 public static boolean isUIScaleEnabled() { 292 return uiScaleEnabled; 293 } 294 getDebugScale()295 public static double getDebugScale() { 296 return debugScale; 297 } 298 getScaleFactor(String propertyName)299 public static double getScaleFactor(String propertyName) { 300 301 String scaleFactor = AccessController.doPrivileged( 302 new GetPropertyAction(propertyName, "-1")); 303 304 if (scaleFactor == null || scaleFactor.equals("-1")) { 305 return -1; 306 } 307 308 try { 309 double units = 1.0; 310 311 if (scaleFactor.endsWith("x")) { 312 scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 1); 313 } else if (scaleFactor.endsWith("dpi")) { 314 units = 96; 315 scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 3); 316 } else if (scaleFactor.endsWith("%")) { 317 units = 100; 318 scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 1); 319 } 320 321 double scale = Double.parseDouble(scaleFactor); 322 return scale <= 0 ? -1 : scale / units; 323 } catch (NumberFormatException ignored) { 324 return -1; 325 } 326 } 327 328 /** 329 * Returns the graphics configuration which bounds contain the given point. 330 * 331 * @param current the default configuration which is checked in the first 332 * place 333 * @param x the x coordinate of the given point 334 * @param y the y coordinate of the given point 335 * @return the graphics configuration 336 */ getGraphicsConfigurationAtPoint( GraphicsConfiguration current, double x, double y)337 public static GraphicsConfiguration getGraphicsConfigurationAtPoint( 338 GraphicsConfiguration current, double x, double y) { 339 if (current.getBounds().contains(x, y)) { 340 return current; 341 } 342 GraphicsEnvironment env = getLocalGraphicsEnvironment(); 343 for (GraphicsDevice device : env.getScreenDevices()) { 344 GraphicsConfiguration config = device.getDefaultConfiguration(); 345 if (config.getBounds().contains(x, y)) { 346 return config; 347 } 348 } 349 return current; 350 } 351 352 /** 353 * Returns the bounds of the graphics configuration in device space. 354 * 355 * @param config the graphics configuration which bounds are requested 356 * @return the bounds of the area covered by this 357 * {@code GraphicsConfiguration} in device space (pixels) 358 */ getGCDeviceBounds(GraphicsConfiguration config)359 public static Rectangle getGCDeviceBounds(GraphicsConfiguration config) { 360 AffineTransform tx = config.getDefaultTransform(); 361 Rectangle bounds = config.getBounds(); 362 bounds.width *= tx.getScaleX(); 363 bounds.height *= tx.getScaleY(); 364 return bounds; 365 } 366 367 /** 368 * Converts the size (w, h) from the device space to the user's space using 369 * passed graphics configuration. 370 * 371 * @param gc the graphics configuration to be used for transformation 372 * @param w the width in the device space 373 * @param h the height in the device space 374 * @return the size in the user's space 375 */ toUserSpace(GraphicsConfiguration gc, int w, int h)376 public static Dimension toUserSpace(GraphicsConfiguration gc, 377 int w, int h) { 378 AffineTransform tx = gc.getDefaultTransform(); 379 return new Dimension( 380 Region.clipRound(w / tx.getScaleX()), 381 Region.clipRound(h / tx.getScaleY()) 382 ); 383 } 384 385 /** 386 * Converts absolute coordinates from the user's space to the device space 387 * using appropriate device transformation. 388 * 389 * @param x absolute coordinate in the user's space 390 * @param y absolute coordinate in the user's space 391 * @return the point which uses device space (pixels) 392 */ toDeviceSpaceAbs(int x, int y)393 public static Point toDeviceSpaceAbs(int x, int y) { 394 GraphicsConfiguration gc = getLocalGraphicsEnvironment() 395 .getDefaultScreenDevice().getDefaultConfiguration(); 396 gc = getGraphicsConfigurationAtPoint(gc, x, y); 397 return toDeviceSpaceAbs(gc, x, y, 0, 0).getLocation(); 398 } 399 400 /** 401 * Converts the rectangle from the user's space to the device space using 402 * appropriate device transformation. 403 * 404 * @param rect the rectangle in the user's space 405 * @return the rectangle which uses device space (pixels) 406 */ toDeviceSpaceAbs(Rectangle rect)407 public static Rectangle toDeviceSpaceAbs(Rectangle rect) { 408 GraphicsConfiguration gc = getLocalGraphicsEnvironment() 409 .getDefaultScreenDevice().getDefaultConfiguration(); 410 gc = getGraphicsConfigurationAtPoint(gc, rect.x, rect.y); 411 return toDeviceSpaceAbs(gc, rect.x, rect.y, rect.width, rect.height); 412 } 413 414 /** 415 * Converts absolute coordinates (x, y) and the size (w, h) from the user's 416 * space to the device space using passed graphics configuration. 417 * 418 * @param gc the graphics configuration to be used for transformation 419 * @param x absolute coordinate in the user's space 420 * @param y absolute coordinate in the user's space 421 * @param w the width in the user's space 422 * @param h the height in the user's space 423 * @return the rectangle which uses device space (pixels) 424 */ toDeviceSpaceAbs(GraphicsConfiguration gc, int x, int y, int w, int h)425 public static Rectangle toDeviceSpaceAbs(GraphicsConfiguration gc, 426 int x, int y, int w, int h) { 427 AffineTransform tx = gc.getDefaultTransform(); 428 Rectangle screen = gc.getBounds(); 429 return new Rectangle( 430 screen.x + Region.clipRound((x - screen.x) * tx.getScaleX()), 431 screen.y + Region.clipRound((y - screen.y) * tx.getScaleY()), 432 Region.clipRound(w * tx.getScaleX()), 433 Region.clipRound(h * tx.getScaleY()) 434 ); 435 } 436 437 /** 438 * Converts coordinates from the user's space to the device space using 439 * appropriate device transformation. 440 * 441 * @param x coordinate in the user's space 442 * @param y coordinate in the user's space 443 * @return the point which uses device space (pixels) 444 */ toDeviceSpace(int x, int y)445 public static Point toDeviceSpace(int x, int y) { 446 GraphicsConfiguration gc = getLocalGraphicsEnvironment() 447 .getDefaultScreenDevice().getDefaultConfiguration(); 448 gc = getGraphicsConfigurationAtPoint(gc, x, y); 449 return toDeviceSpace(gc, x, y, 0, 0).getLocation(); 450 } 451 452 /** 453 * Converts coordinates (x, y) and the size (w, h) from the user's 454 * space to the device space using passed graphics configuration. 455 * 456 * @param gc the graphics configuration to be used for transformation 457 * @param x coordinate in the user's space 458 * @param y coordinate in the user's space 459 * @param w the width in the user's space 460 * @param h the height in the user's space 461 * @return the rectangle which uses device space (pixels) 462 */ toDeviceSpace(GraphicsConfiguration gc, int x, int y, int w, int h)463 public static Rectangle toDeviceSpace(GraphicsConfiguration gc, 464 int x, int y, int w, int h) { 465 AffineTransform tx = gc.getDefaultTransform(); 466 return new Rectangle( 467 Region.clipRound(x * tx.getScaleX()), 468 Region.clipRound(y * tx.getScaleY()), 469 Region.clipRound(w * tx.getScaleX()), 470 Region.clipRound(h * tx.getScaleY()) 471 ); 472 } 473 } 474