| /* | |
| * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.java2d; | |
| import java.awt.AWTError; | |
| import java.awt.Color; | |
| import java.awt.Dimension; | |
| import java.awt.Font; | |
| import java.awt.Graphics2D; | |
| import java.awt.GraphicsConfiguration; | |
| import java.awt.GraphicsDevice; | |
| import java.awt.GraphicsEnvironment; | |
| import java.awt.Insets; | |
| import java.awt.Point; | |
| import java.awt.Rectangle; | |
| import java.awt.Toolkit; | |
| import java.awt.geom.AffineTransform; | |
| import java.awt.image.BufferedImage; | |
| import java.awt.peer.ComponentPeer; | |
| import java.util.Locale; | |
| import java.util.TreeMap; | |
| import sun.awt.DisplayChangedListener; | |
| import sun.awt.SunDisplayChanger; | |
| import sun.font.FontManager; | |
| import sun.font.FontManagerFactory; | |
| import sun.font.FontManagerForSGE; | |
| import sun.java2d.pipe.Region; | |
| /** | |
| * This is an implementation of a GraphicsEnvironment object for the | |
| * default local GraphicsEnvironment. | |
| * | |
| * @see GraphicsDevice | |
| * @see GraphicsConfiguration | |
| */ | |
| public abstract class SunGraphicsEnvironment extends GraphicsEnvironment | |
| implements DisplayChangedListener { | |
| /** Establish the default font to be used by SG2D. */ | |
| private final Font defaultFont = new Font(Font.DIALOG, Font.PLAIN, 12); | |
| private static final boolean uiScaleEnabled | |
| = "true".equals(System.getProperty("sun.java2d.uiScale.enabled", "true")); | |
| private static final double debugScale = | |
| uiScaleEnabled ? getScaleFactor("sun.java2d.uiScale") : -1; | |
| protected GraphicsDevice[] screens; | |
| /** | |
| * Returns an array of all of the screen devices. | |
| */ | |
| public synchronized GraphicsDevice[] getScreenDevices() { | |
| GraphicsDevice[] ret = screens; | |
| if (ret == null) { | |
| int num = getNumScreens(); | |
| ret = new GraphicsDevice[num]; | |
| for (int i = 0; i < num; i++) { | |
| ret[i] = makeScreenDevice(i); | |
| } | |
| screens = ret; | |
| } | |
| return ret; | |
| } | |
| /** | |
| * Returns the number of screen devices of this graphics environment. | |
| * | |
| * @return the number of screen devices of this graphics environment | |
| */ | |
| protected abstract int getNumScreens(); | |
| /** | |
| * Create and return the screen device with the specified number. The | |
| * device with number {@code 0} will be the default device (returned | |
| * by {@link #getDefaultScreenDevice()}. | |
| * | |
| * @param screennum the number of the screen to create | |
| * | |
| * @return the created screen device | |
| */ | |
| protected abstract GraphicsDevice makeScreenDevice(int screennum); | |
| /** | |
| * Returns the default screen graphics device. | |
| */ | |
| public GraphicsDevice getDefaultScreenDevice() { | |
| GraphicsDevice[] screens = getScreenDevices(); | |
| if (screens.length == 0) { | |
| throw new AWTError("no screen devices"); | |
| } | |
| return screens[0]; | |
| } | |
| /** | |
| * Returns a Graphics2D object for rendering into the | |
| * given BufferedImage. | |
| * @throws NullPointerException if BufferedImage argument is null | |
| */ | |
| public Graphics2D createGraphics(BufferedImage img) { | |
| if (img == null) { | |
| throw new NullPointerException("BufferedImage cannot be null"); | |
| } | |
| SurfaceData sd = SurfaceData.getPrimarySurfaceData(img); | |
| return new SunGraphics2D(sd, Color.white, Color.black, defaultFont); | |
| } | |
| public static FontManagerForSGE getFontManagerForSGE() { | |
| FontManager fm = FontManagerFactory.getInstance(); | |
| return (FontManagerForSGE) fm; | |
| } | |
| /* Modifies the behaviour of a subsequent call to preferLocaleFonts() | |
| * to use Mincho instead of Gothic for dialoginput in JA locales | |
| * on windows. Not needed on other platforms. | |
| * | |
| * @deprecated as of JDK9. To be removed in a future release | |
| */ | |
| @Deprecated | |
| public static void useAlternateFontforJALocales() { | |
| getFontManagerForSGE().useAlternateFontforJALocales(); | |
| } | |
| /** | |
| * Returns all fonts available in this environment. | |
| */ | |
| public Font[] getAllFonts() { | |
| FontManagerForSGE fm = getFontManagerForSGE(); | |
| Font[] installedFonts = fm.getAllInstalledFonts(); | |
| Font[] created = fm.getCreatedFonts(); | |
| if (created == null || created.length == 0) { | |
| return installedFonts; | |
| } else { | |
| int newlen = installedFonts.length + created.length; | |
| Font [] fonts = java.util.Arrays.copyOf(installedFonts, newlen); | |
| System.arraycopy(created, 0, fonts, | |
| installedFonts.length, created.length); | |
| return fonts; | |
| } | |
| } | |
| public String[] getAvailableFontFamilyNames(Locale requestedLocale) { | |
| FontManagerForSGE fm = getFontManagerForSGE(); | |
| String[] installed = fm.getInstalledFontFamilyNames(requestedLocale); | |
| /* Use a new TreeMap as used in getInstalledFontFamilyNames | |
| * and insert all the keys in lower case, so that the sort order | |
| * is the same as the installed families. This preserves historical | |
| * behaviour and inserts new families in the right place. | |
| * It would have been marginally more efficient to directly obtain | |
| * the tree map and just insert new entries, but not so much as | |
| * to justify the extra internal interface. | |
| */ | |
| TreeMap<String, String> map = fm.getCreatedFontFamilyNames(); | |
| if (map == null || map.size() == 0) { | |
| return installed; | |
| } else { | |
| for (int i=0; i<installed.length; i++) { | |
| map.put(installed[i].toLowerCase(requestedLocale), | |
| installed[i]); | |
| } | |
| String[] retval = map.values().toArray(new String[0]); | |
| return retval; | |
| } | |
| } | |
| public String[] getAvailableFontFamilyNames() { | |
| return getAvailableFontFamilyNames(Locale.getDefault()); | |
| } | |
| /** | |
| * Return the bounds of a GraphicsDevice, less its screen insets. | |
| * See also java.awt.GraphicsEnvironment.getUsableBounds(); | |
| */ | |
| public static Rectangle getUsableBounds(GraphicsDevice gd) { | |
| GraphicsConfiguration gc = gd.getDefaultConfiguration(); | |
| Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc); | |
| Rectangle usableBounds = gc.getBounds(); | |
| usableBounds.x += insets.left; | |
| usableBounds.y += insets.top; | |
| usableBounds.width -= (insets.left + insets.right); | |
| usableBounds.height -= (insets.top + insets.bottom); | |
| return usableBounds; | |
| } | |
| /** | |
| * From the DisplayChangedListener interface; called | |
| * when the display mode has been changed. | |
| */ | |
| public void displayChanged() { | |
| // notify screens in device array to do display update stuff | |
| for (GraphicsDevice gd : getScreenDevices()) { | |
| if (gd instanceof DisplayChangedListener) { | |
| ((DisplayChangedListener) gd).displayChanged(); | |
| } | |
| } | |
| // notify SunDisplayChanger list (e.g. VolatileSurfaceManagers and | |
| // SurfaceDataProxies) about the display change event | |
| displayChanger.notifyListeners(); | |
| } | |
| /** | |
| * Part of the DisplayChangedListener interface: | |
| * propagate this event to listeners | |
| */ | |
| public void paletteChanged() { | |
| displayChanger.notifyPaletteChanged(); | |
| } | |
| /** | |
| * Returns true when the display is local, false for remote displays. | |
| * | |
| * @return true when the display is local, false for remote displays | |
| */ | |
| public abstract boolean isDisplayLocal(); | |
| /* | |
| * ----DISPLAY CHANGE SUPPORT---- | |
| */ | |
| protected SunDisplayChanger displayChanger = new SunDisplayChanger(); | |
| /** | |
| * Add a DisplayChangeListener to be notified when the display settings | |
| * are changed. | |
| */ | |
| public void addDisplayChangedListener(DisplayChangedListener client) { | |
| displayChanger.add(client); | |
| } | |
| /** | |
| * Remove a DisplayChangeListener from Win32GraphicsEnvironment | |
| */ | |
| public void removeDisplayChangedListener(DisplayChangedListener client) { | |
| displayChanger.remove(client); | |
| } | |
| /* | |
| * ----END DISPLAY CHANGE SUPPORT---- | |
| */ | |
| /** | |
| * Returns true if FlipBufferStrategy with COPIED buffer contents | |
| * is preferred for this peer's GraphicsConfiguration over | |
| * BlitBufferStrategy, false otherwise. | |
| * | |
| * The reason FlipBS could be preferred is that in some configurations | |
| * an accelerated copy to the screen is supported (like Direct3D 9) | |
| * | |
| * @return true if flip strategy should be used, false otherwise | |
| */ | |
| public boolean isFlipStrategyPreferred(ComponentPeer peer) { | |
| return false; | |
| } | |
| public static boolean isUIScaleEnabled() { | |
| return uiScaleEnabled; | |
| } | |
| public static double getDebugScale() { | |
| return debugScale; | |
| } | |
| public static double getScaleFactor(String propertyName) { | |
| String scaleFactor = System.getProperty(propertyName, "-1"); | |
| if (scaleFactor == null || scaleFactor.equals("-1")) { | |
| return -1; | |
| } | |
| try { | |
| double units = 1.0; | |
| if (scaleFactor.endsWith("x")) { | |
| scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 1); | |
| } else if (scaleFactor.endsWith("dpi")) { | |
| units = 96; | |
| scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 3); | |
| } else if (scaleFactor.endsWith("%")) { | |
| units = 100; | |
| scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 1); | |
| } | |
| double scale = Double.parseDouble(scaleFactor); | |
| return scale <= 0 ? -1 : scale / units; | |
| } catch (NumberFormatException ignored) { | |
| return -1; | |
| } | |
| } | |
| /** | |
| * Returns the graphics configuration which bounds contain the given point. | |
| * | |
| * @param current the default configuration which is checked in the first | |
| * place | |
| * @param x the x coordinate of the given point | |
| * @param y the y coordinate of the given point | |
| * @return the graphics configuration | |
| */ | |
| public static GraphicsConfiguration getGraphicsConfigurationAtPoint( | |
| GraphicsConfiguration current, double x, double y) { | |
| if (current.getBounds().contains(x, y)) { | |
| return current; | |
| } | |
| GraphicsEnvironment env = getLocalGraphicsEnvironment(); | |
| for (GraphicsDevice device : env.getScreenDevices()) { | |
| GraphicsConfiguration config = device.getDefaultConfiguration(); | |
| if (config.getBounds().contains(x, y)) { | |
| return config; | |
| } | |
| } | |
| return current; | |
| } | |
| /** | |
| * Returns the bounds of the graphics configuration in device space. | |
| * | |
| * @param config the graphics configuration which bounds are requested | |
| * @return the bounds of the area covered by this | |
| * {@code GraphicsConfiguration} in device space (pixels) | |
| */ | |
| public static Rectangle getGCDeviceBounds(GraphicsConfiguration config) { | |
| AffineTransform tx = config.getDefaultTransform(); | |
| Rectangle bounds = config.getBounds(); | |
| bounds.width *= tx.getScaleX(); | |
| bounds.height *= tx.getScaleY(); | |
| return bounds; | |
| } | |
| /** | |
| * Converts the size (w, h) from the device space to the user's space using | |
| * passed graphics configuration. | |
| * | |
| * @param gc the graphics configuration to be used for transformation | |
| * @param w the width in the device space | |
| * @param h the height in the device space | |
| * @return the size in the user's space | |
| */ | |
| public static Dimension toUserSpace(GraphicsConfiguration gc, | |
| int w, int h) { | |
| AffineTransform tx = gc.getDefaultTransform(); | |
| return new Dimension( | |
| Region.clipRound(w / tx.getScaleX()), | |
| Region.clipRound(h / tx.getScaleY()) | |
| ); | |
| } | |
| /** | |
| * Converts absolute coordinates from the user's space to the device space | |
| * using appropriate device transformation. | |
| * | |
| * @param x absolute coordinate in the user's space | |
| * @param y absolute coordinate in the user's space | |
| * @return the point which uses device space (pixels) | |
| */ | |
| public static Point toDeviceSpaceAbs(int x, int y) { | |
| GraphicsConfiguration gc = getLocalGraphicsEnvironment() | |
| .getDefaultScreenDevice().getDefaultConfiguration(); | |
| gc = getGraphicsConfigurationAtPoint(gc, x, y); | |
| return toDeviceSpaceAbs(gc, x, y, 0, 0).getLocation(); | |
| } | |
| /** | |
| * Converts the rectangle from the user's space to the device space using | |
| * appropriate device transformation. | |
| * | |
| * @param rect the rectangle in the user's space | |
| * @return the rectangle which uses device space (pixels) | |
| */ | |
| public static Rectangle toDeviceSpaceAbs(Rectangle rect) { | |
| GraphicsConfiguration gc = getLocalGraphicsEnvironment() | |
| .getDefaultScreenDevice().getDefaultConfiguration(); | |
| gc = getGraphicsConfigurationAtPoint(gc, rect.x, rect.y); | |
| return toDeviceSpaceAbs(gc, rect.x, rect.y, rect.width, rect.height); | |
| } | |
| /** | |
| * Converts absolute coordinates (x, y) and the size (w, h) from the user's | |
| * space to the device space using passed graphics configuration. | |
| * | |
| * @param gc the graphics configuration to be used for transformation | |
| * @param x absolute coordinate in the user's space | |
| * @param y absolute coordinate in the user's space | |
| * @param w the width in the user's space | |
| * @param h the height in the user's space | |
| * @return the rectangle which uses device space (pixels) | |
| */ | |
| public static Rectangle toDeviceSpaceAbs(GraphicsConfiguration gc, | |
| int x, int y, int w, int h) { | |
| AffineTransform tx = gc.getDefaultTransform(); | |
| Rectangle screen = gc.getBounds(); | |
| return new Rectangle( | |
| screen.x + Region.clipRound((x - screen.x) * tx.getScaleX()), | |
| screen.y + Region.clipRound((y - screen.y) * tx.getScaleY()), | |
| Region.clipRound(w * tx.getScaleX()), | |
| Region.clipRound(h * tx.getScaleY()) | |
| ); | |
| } | |
| /** | |
| * Converts coordinates from the user's space to the device space using | |
| * appropriate device transformation. | |
| * | |
| * @param x coordinate in the user's space | |
| * @param y coordinate in the user's space | |
| * @return the point which uses device space (pixels) | |
| */ | |
| public static Point toDeviceSpace(int x, int y) { | |
| GraphicsConfiguration gc = getLocalGraphicsEnvironment() | |
| .getDefaultScreenDevice().getDefaultConfiguration(); | |
| gc = getGraphicsConfigurationAtPoint(gc, x, y); | |
| return toDeviceSpace(gc, x, y, 0, 0).getLocation(); | |
| } | |
| /** | |
| * Converts coordinates (x, y) and the size (w, h) from the user's | |
| * space to the device space using passed graphics configuration. | |
| * | |
| * @param gc the graphics configuration to be used for transformation | |
| * @param x coordinate in the user's space | |
| * @param y coordinate in the user's space | |
| * @param w the width in the user's space | |
| * @param h the height in the user's space | |
| * @return the rectangle which uses device space (pixels) | |
| */ | |
| public static Rectangle toDeviceSpace(GraphicsConfiguration gc, | |
| int x, int y, int w, int h) { | |
| AffineTransform tx = gc.getDefaultTransform(); | |
| return new Rectangle( | |
| Region.clipRound(x * tx.getScaleX()), | |
| Region.clipRound(y * tx.getScaleY()), | |
| Region.clipRound(w * tx.getScaleX()), | |
| Region.clipRound(h * tx.getScaleY()) | |
| ); | |
| } | |
| } |