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