Ticket #20706: antialias_v3.patch

File antialias_v3.patch, 10.8 KB (added by nvarner, 4 years ago)
  • src/org/openstreetmap/josm/gui/MainApplication.java

     
    148148import org.openstreetmap.josm.spi.lifecycle.InitStatusListener;
    149149import org.openstreetmap.josm.spi.lifecycle.Lifecycle;
    150150import org.openstreetmap.josm.spi.preferences.Config;
     151import org.openstreetmap.josm.tools.AntialiasingUtil;
    151152import org.openstreetmap.josm.tools.FontsManager;
    152153import org.openstreetmap.josm.tools.GBC;
    153154import org.openstreetmap.josm.tools.Http1Client;
     
    10331034        }
    10341035        // Disable automatic POST retry after 5 minutes, see #17882 / https://bugs.openjdk.java.net/browse/JDK-6382788
    10351036        Utils.updateSystemProperty("sun.net.http.retryPost", "false");
     1037        // Force text antialiasing, not including mappaint text, when antialiasing is not enabled by default on X11
     1038        // See #20706
     1039        if (AntialiasingUtil.isDisabledX11() && !AntialiasingUtil.tryEnableX11()) {
     1040            Logging.warn("Antialiasing could not be enabled");
     1041        }
    10361042    }
    10371043
    10381044    /**
  • src/org/openstreetmap/josm/gui/widgets/JosmEditorPane.java

     
    33
    44import java.awt.Color;
    55import java.awt.Font;
     6import java.awt.Graphics;
     7import java.awt.Graphics2D;
     8import java.awt.RenderingHints;
    69import java.io.IOException;
    710import java.io.InputStream;
    811import java.net.URL;
     
    8689        return conn.getContent();
    8790    }
    8891
     92    @Override
     93    public void paintComponent(Graphics g) {
     94        // Force antialiasing within the JosmEditorPane for antialiased bullet points
     95        Graphics2D g2d = (Graphics2D) g.create();
     96        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
     97        super.paintComponent(g2d);
     98        g2d.dispose();
     99    }
     100
    89101    /**
    90102     * Adapts a {@link JEditorPane} to be used as a powerful replacement of {@link javax.swing.JLabel}.
    91103     * @param pane The editor pane to adapt
  • src/org/openstreetmap/josm/tools/AntialiasingUtil.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.tools;
     3
     4import java.awt.Toolkit;
     5import java.awt.RenderingHints;
     6import java.lang.reflect.Field;
     7import java.lang.reflect.Method;
     8import java.util.Map;
     9
     10/**
     11 * Utility class to detect and enable antialiasing on systems which do not enable it by default
     12 * @since xxx
     13 */
     14public final class AntialiasingUtil {
     15
     16    private AntialiasingUtil() {
     17        // Hide default constructor for util classes
     18    }
     19
     20    /**
     21     * Determines if the current system is running X11 and has antialiasing disabled
     22     */
     23    public static boolean isDisabledX11() {
     24        Toolkit toolkit = Toolkit.getDefaultToolkit();
     25        return ReflectionUtils.extendsClassWithName(toolkit.getClass(), "sun.awt.X11.XToolkit")
     26                && toolkit.getDesktopProperty("awt.font.desktophints") == null;
     27    }
     28
     29    /**
     30     * Attempts to enable antialiasing on X11
     31     * @return true if enabling antialiasing was successful, otherwise false
     32     */
     33    public static boolean tryEnableX11() {
     34        try {
     35            Utils.updateSystemProperty("awt.useSystemAAFontSettings", "on");
     36
     37            Toolkit toolkit = Toolkit.getDefaultToolkit();
     38
     39            // Unset cached desktop hints
     40            Map<String, Object> desktopProperties = getToolkitDesktopProperties(toolkit);
     41            desktopProperties.remove("awt.font.desktophints");
     42
     43            // Unset flag so new font hints look for new value of awt.useSystemAAFontSettings
     44            Field checkedSystemAAFontSettings = getSunToolkitCheckedSystemAAFontSettings(toolkit);
     45            checkedSystemAAFontSettings.setBoolean(toolkit, false);
     46
     47            // Get antialiasing font hints
     48            Method getDesktopFontHints = getSunToolkitGetDesktopFontHints(toolkit);
     49            Object renderingHints = getDesktopFontHints.invoke(null);
     50
     51            desktopProperties.put("awt.font.desktophints", renderingHints);
     52        } catch (ReflectiveOperationException | RuntimeException e) {
     53            Logging.log(Logging.LEVEL_WARN, e);
     54            return false;
     55        }
     56        return true;
     57    }
     58
     59    /**
     60     * Get the value of the desktopProperties field from {@link java.awt.Toolkit} through reflection. May emit an
     61     * illegal reflective access warning.
     62     * @param toolkit the toolkit to get the desktopProperties from
     63     * @return the value of the desktopProperties field from toolkit
     64     * @throws ReflectiveOperationException if the field could not be found
     65     * @throws RuntimeException if the field could not be set accessible
     66     */
     67    private static Map<String, Object> getToolkitDesktopProperties(Toolkit toolkit) throws ReflectiveOperationException, RuntimeException {
     68        Field desktopPropertiesField = Toolkit.class.getDeclaredField("desktopProperties");
     69        ReflectionUtils.setObjectsAccessible(desktopPropertiesField);
     70        @SuppressWarnings("unchecked") // desktopProperties has type Map<String, Object> and is protected
     71        Map<String, Object> desktopProperties = (Map<String, Object>) desktopPropertiesField.get(toolkit);
     72        return desktopProperties;
     73    }
     74
     75    /**
     76     * Get the boolean checkedSystemAAFontSettings field from sun.awt.SunToolkit through reflection. May emit an
     77     * illegal reflective access warning.
     78     * @param toolkit the toolkit to get the checkedSystemAAFontSettings field from
     79     * @return the checkedSystemAAFontSettings field or null if it could not be found
     80     * @throws RuntimeException if the field could not be set accessible
     81     */
     82    private static Field getSunToolkitCheckedSystemAAFontSettings(Toolkit toolkit) throws RuntimeException {
     83        Field field = ReflectionUtils.getSuperclassField(toolkit.getClass(), "checkedSystemAAFontSettings", boolean.class);
     84        ReflectionUtils.setObjectsAccessible(field);
     85        return field;
     86    }
     87
     88    /**
     89     * Get the getDesktopFontHints method or no arguments returning {@link RenderingHints} from sun.awt.SunToolkit
     90     * through reflection. May emit an illegal reflective access warning.
     91     * @param toolkit the toolkit to get the getDesktopFontHints method from
     92     * @return the getDesktopFontHints method or null if it could not be found
     93     * @throws RuntimeException if the method could not be set accessible
     94     */
     95    private static Method getSunToolkitGetDesktopFontHints(Toolkit toolkit) throws RuntimeException {
     96        Method getDesktopFontHints = ReflectionUtils.getInheritedMethod(
     97                toolkit.getClass(),
     98                "getDesktopFontHints",
     99                new Class[] {},
     100                RenderingHints.class
     101        );
     102        ReflectionUtils.setObjectsAccessible(getDesktopFontHints);
     103        return getDesktopFontHints;
     104    }
     105}
  • src/org/openstreetmap/josm/tools/ReflectionUtils.java

     
    22package org.openstreetmap.josm.tools;
    33
    44import java.lang.reflect.AccessibleObject;
     5import java.lang.reflect.Field;
     6import java.lang.reflect.Method;
    57import java.security.AccessController;
    68import java.security.PrivilegedAction;
     9import java.util.Arrays;
    710import java.util.Collection;
     11import java.util.Optional;
    812import java.util.function.Function;
    913
    1014import org.openstreetmap.josm.plugins.PluginHandler;
     
    7074        }
    7175        return null;
    7276    }
     77
     78    /**
     79     * Get the matching method implemented directly on the class or inherited from a superclass (or superclass of a superclass...).
     80     * @param searchOn class to start the search from
     81     * @param name the name that the matching method must have
     82     * @param parameterTypes the types the parameters of the matching method must have
     83     * @param returnType the type the matching method must return
     84     * @return the first matching method found, starting from the given class and proceeding through superclasses, or
     85     *  null if no match was found
     86     * @since xxx
     87     */
     88    public static Method getInheritedMethod(Class<?> searchOn, String name, Class<?>[] parameterTypes, Class<?> returnType) {
     89        while (searchOn != null) {
     90            Optional<Method> firstMatch = Arrays.stream(searchOn.getDeclaredMethods())
     91                    .filter(method -> methodMatches(method, name, parameterTypes, returnType))
     92                    .findFirst();
     93            if (firstMatch.isPresent()) {
     94                return firstMatch.get();
     95            }
     96            searchOn = searchOn.getSuperclass();
     97        }
     98        return null;
     99    }
     100
     101    private static boolean methodMatches(Method method, String expectedName, Class<?>[] expectedParameterTypes, Class<?> expectedReturnType) {
     102        return method.getName().equals(expectedName)
     103                && Arrays.equals(method.getParameterTypes(), expectedParameterTypes)
     104                && method.getReturnType().equals(expectedReturnType);
     105    }
     106
     107    /**
     108     * Get the matching field which is part of the class or a superclass (or superclass of a superclass...).
     109     * @param searchOn class to start the search from
     110     * @param name the name that the matching field must have
     111     * @param type the type that the matching field must have
     112     * @return the first matching field found, starting from the given class and proceeding through superclasses, or
     113     *  null if no match was found
     114     * @since xxx
     115     */
     116    public static Field getSuperclassField(Class<?> searchOn, String name, Class<?> type) {
     117        while (searchOn != null) {
     118            Optional<Field> firstMatch = Arrays.stream(searchOn.getDeclaredFields())
     119                    .filter(field -> field.getName().equals(name) && field.getType().equals(type))
     120                    .findFirst();
     121            if (firstMatch.isPresent()) {
     122                return firstMatch.get();
     123            }
     124            searchOn = searchOn.getSuperclass();
     125        }
     126        return null;
     127    }
     128
     129    public static boolean extendsClassWithName(Class<?> doesExtend, String className) {
     130        while (doesExtend != null) {
     131            if (doesExtend.getName().equals(className)) {
     132                return true;
     133            }
     134            doesExtend = doesExtend.getSuperclass();
     135        }
     136        return false;
     137    }
    73138}