# HG changeset patch # User alexsch # Date 1440417996 -14400 # Node ID e00f713e6103be11ad6cf8cb052faa25b3a9e627 # Parent e07799997e1fce56ce3fd6bd7f15b00a536a8f06 6302464: Allow programmatic enabling of subpixel anti-aliasing in Swing on ANY platform Reviewed-by: serb, azvegint diff -r e07799997e1f -r e00f713e6103 jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaLookAndFeel.java --- a/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaLookAndFeel.java Fri Aug 21 20:59:07 2015 +0300 +++ b/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaLookAndFeel.java Mon Aug 24 16:06:36 2015 +0400 @@ -991,9 +991,7 @@ "Tree.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[]{"ESCAPE", "cancel"}),}; table.putDefaults(defaults); - - Object aaTextInfo = SwingUtilities2.AATextInfo.getAATextInfo(true); - table.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo); + SwingUtilities2.putAATextInfo(true, table); } protected void initSystemColorDefaults(final UIDefaults table) { diff -r e07799997e1f -r e00f713e6103 jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java --- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java Fri Aug 21 20:59:07 2015 +0300 +++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java Mon Aug 24 16:06:36 2015 +0400 @@ -26,7 +26,6 @@ package com.sun.java.swing.plaf.gtk; import java.awt.*; -import java.awt.event.*; import java.beans.*; import java.io.File; import java.lang.ref.*; @@ -41,6 +40,8 @@ import com.sun.java.swing.plaf.gtk.GTKConstants.PositionType; import com.sun.java.swing.plaf.gtk.GTKConstants.StateType; +import java.util.HashMap; +import java.util.Map; import sun.awt.SunToolkit; import sun.awt.UNIXToolkit; import sun.awt.OSInfo; @@ -61,7 +62,7 @@ * We should assume ON - or some variation of ON as no GTK desktop * ships with it OFF. */ - static Object aaTextInfo; + static Map aaTextInfo; /** * Solaris, or Linux with Sun JDS in a CJK Locale. @@ -1337,7 +1338,9 @@ if (fallbackFont != null) { table.put("TitledBorder.font", fallbackFont); } - table.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo); + if (aaTextInfo != null) { + table.putAll(aaTextInfo); + } } protected void initSystemColorDefaults(UIDefaults table) { @@ -1477,7 +1480,8 @@ * XRender. */ gtkAAFontSettingsCond = !isSunCJK && SwingUtilities2.isLocalDisplay(); - aaTextInfo = SwingUtilities2.AATextInfo.getAATextInfo(gtkAAFontSettingsCond); + aaTextInfo = new HashMap<>(2); + SwingUtilities2.putAATextInfo(gtkAAFontSettingsCond, aaTextInfo); } static ReferenceQueue queue = new ReferenceQueue(); diff -r e07799997e1f -r e00f713e6103 jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java --- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java Fri Aug 21 20:59:07 2015 +0300 +++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java Mon Aug 24 16:06:36 2015 +0400 @@ -39,6 +39,8 @@ import sun.swing.plaf.synth.SynthIcon; import com.sun.java.swing.plaf.gtk.GTKEngine.WidgetType; +import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING; +import static java.awt.RenderingHints.KEY_TEXT_LCD_CONTRAST; /** * @@ -115,10 +117,12 @@ @Override public void installDefaults(SynthContext context) { super.installDefaults(context); - if (!context.getRegion().isSubregion()) { - context.getComponent().putClientProperty( - SwingUtilities2.AA_TEXT_PROPERTY_KEY, - GTKLookAndFeel.aaTextInfo); + Map aaTextInfo = GTKLookAndFeel.aaTextInfo; + if (aaTextInfo != null && !context.getRegion().isSubregion()) { + context.getComponent().putClientProperty(KEY_TEXT_ANTIALIASING, + aaTextInfo.get(KEY_TEXT_ANTIALIASING)); + context.getComponent().putClientProperty(KEY_TEXT_LCD_CONTRAST, + aaTextInfo.get(KEY_TEXT_LCD_CONTRAST)); } } diff -r e07799997e1f -r e00f713e6103 jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java --- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java Fri Aug 21 20:59:07 2015 +0300 +++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java Mon Aug 24 16:06:36 2015 +0400 @@ -556,8 +556,7 @@ * for both client property and UIDefaults. * Also need to set up listeners for changes in these settings. */ - Object aaTextInfo = SwingUtilities2.AATextInfo.getAATextInfo(true); - table.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo); + SwingUtilities2.putAATextInfo(true, table); this.aaSettings = new FontDesktopProperty(SunToolkit.DESKTOPFONTHINTS); } @@ -2402,9 +2401,8 @@ } protected void updateUI() { - Object aaTextInfo = SwingUtilities2.AATextInfo.getAATextInfo(true); UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - defaults.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo); + SwingUtilities2.putAATextInfo(true, defaults); super.updateUI(); } } diff -r e07799997e1f -r e00f713e6103 jdk/src/java.desktop/share/classes/javax/swing/JComponent.java --- a/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java Fri Aug 21 20:59:07 2015 +0300 +++ b/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java Mon Aug 24 16:06:36 2015 +0400 @@ -377,7 +377,8 @@ /** * AA text hints. */ - transient private Object aaTextInfo; + transient private Object aaHint; + transient private Object lcdRenderingHint; static Graphics safelyGetGraphics(Component c) { return safelyGetGraphics(c, SwingUtilities.getRoot(c)); @@ -655,8 +656,10 @@ uninstallUIAndProperties(); // aaText shouldn't persist between look and feels, reset it. - aaTextInfo = - UIManager.getDefaults().get(SwingUtilities2.AA_TEXT_PROPERTY_KEY); + aaHint = UIManager.getDefaults().get( + RenderingHints.KEY_TEXT_ANTIALIASING); + lcdRenderingHint = UIManager.getDefaults().get( + RenderingHints.KEY_TEXT_LCD_CONTRAST); ComponentUI oldUI = ui; ui = newUI; if (ui != null) { @@ -4048,8 +4051,10 @@ * @see #putClientProperty */ public final Object getClientProperty(Object key) { - if (key == SwingUtilities2.AA_TEXT_PROPERTY_KEY) { - return aaTextInfo; + if (key == RenderingHints.KEY_TEXT_ANTIALIASING) { + return aaHint; + } else if (key == RenderingHints.KEY_TEXT_LCD_CONTRAST) { + return lcdRenderingHint; } else if (key == SwingUtilities2.COMPONENT_UI_PROPERTY_KEY) { return ui; } @@ -4091,8 +4096,11 @@ * @see #addPropertyChangeListener */ public final void putClientProperty(Object key, Object value) { - if (key == SwingUtilities2.AA_TEXT_PROPERTY_KEY) { - aaTextInfo = value; + if (key == RenderingHints.KEY_TEXT_ANTIALIASING) { + aaHint = value; + return; + } else if (key == RenderingHints.KEY_TEXT_LCD_CONTRAST) { + lcdRenderingHint = value; return; } if (value == null && clientProperties == null) { diff -r e07799997e1f -r e00f713e6103 jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java Fri Aug 21 20:59:07 2015 +0300 +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java Mon Aug 24 16:06:36 2015 +0400 @@ -35,11 +35,11 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; +import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING; +import static java.awt.RenderingHints.KEY_TEXT_LCD_CONTRAST; import sun.swing.DefaultLookup; -import static sun.swing.SwingUtilities2.AA_TEXT_PROPERTY_KEY; - /** * The class that manages a basic title bar *

@@ -217,8 +217,10 @@ } private void updateProperties() { - final Object aaTextInfo = frame.getClientProperty(AA_TEXT_PROPERTY_KEY); - putClientProperty(AA_TEXT_PROPERTY_KEY, aaTextInfo); + putClientProperty(KEY_TEXT_ANTIALIASING, + frame.getClientProperty(KEY_TEXT_ANTIALIASING)); + putClientProperty(KEY_TEXT_LCD_CONTRAST, + frame.getClientProperty(KEY_TEXT_LCD_CONTRAST)); } /** diff -r e07799997e1f -r e00f713e6103 jdk/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java Fri Aug 21 20:59:07 2015 +0300 +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java Mon Aug 24 16:06:36 2015 +0400 @@ -1514,8 +1514,7 @@ flushUnreferenced(); // Remove old listeners boolean lafCond = SwingUtilities2.isLocalDisplay(); - Object aaTextInfo = SwingUtilities2.AATextInfo.getAATextInfo(lafCond); - table.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo); + SwingUtilities2.putAATextInfo(lafCond, table); new AATextListener(this); } @@ -2204,9 +2203,7 @@ } UIDefaults defaults = UIManager.getLookAndFeelDefaults(); boolean lafCond = SwingUtilities2.isLocalDisplay(); - Object aaTextInfo = - SwingUtilities2.AATextInfo.getAATextInfo(lafCond); - defaults.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo); + SwingUtilities2.putAATextInfo(lafCond, defaults); updateUI(); } diff -r e07799997e1f -r e00f713e6103 jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java Fri Aug 21 20:59:07 2015 +0300 +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java Mon Aug 24 16:06:36 2015 +0400 @@ -688,8 +688,7 @@ // enabled antialiasing depending on desktop settings flushUnreferenced(); - Object aaTextInfo = getAATextInfo(); - table.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo); + SwingUtilities2.putAATextInfo(useLAFConditions(), table); new AATextListener(this); if (defaultsMap != null) { @@ -794,7 +793,7 @@ * * @return the text antialiasing information associated to the desktop */ - private static Object getAATextInfo() { + private static boolean useLAFConditions() { String language = Locale.getDefault().getLanguage(); String desktop = AccessController.doPrivileged(new GetPropertyAction("sun.desktop")); @@ -805,10 +804,7 @@ boolean isGnome = "gnome".equals(desktop); boolean isLocal = SwingUtilities2.isLocalDisplay(); - boolean setAA = isLocal && (!isGnome || !isCjkLocale); - - Object aaTextInfo = SwingUtilities2.AATextInfo.getAATextInfo(setAA); - return aaTextInfo; + return isLocal && (!isGnome || !isCjkLocale); } private static ReferenceQueue queue = new ReferenceQueue(); @@ -844,8 +840,7 @@ return; } - Object aaTextInfo = getAATextInfo(); - defaults.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo); + SwingUtilities2.putAATextInfo(useLAFConditions(), defaults); updateUI(); } diff -r e07799997e1f -r e00f713e6103 jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java --- a/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Fri Aug 21 20:59:07 2015 +0300 +++ b/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Mon Aug 24 16:06:36 2015 +0400 @@ -103,14 +103,6 @@ new FontRenderContext(null, false, false); /** - * A JComponent client property is used to determine text aa settings. - * To avoid having this property persist between look and feels changes - * the value of the property is set to null in JComponent.setUI - */ - public static final Object AA_TEXT_PROPERTY_KEY = - new StringBuffer("AATextInfoPropertyKey"); - - /** * Attribute key for the content elements. If it is set on an element, the * element is considered to be a line break. */ @@ -123,58 +115,23 @@ private static final StringBuilder SKIP_CLICK_COUNT = new StringBuilder("skipClickCount"); - /* Presently this class assumes default fractional metrics. - * This may need to change to emulate future platform L&Fs. - */ - public static class AATextInfo { - - private static AATextInfo getAATextInfoFromMap(Map hints) { - - Object aaHint = hints.get(KEY_TEXT_ANTIALIASING); - Object contHint = hints.get(KEY_TEXT_LCD_CONTRAST); - - if (aaHint == null || - aaHint == VALUE_TEXT_ANTIALIAS_OFF || - aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) { - return null; - } else { - return new AATextInfo(aaHint, (Integer)contHint); - } - } + @SuppressWarnings("unchecked") + public static void putAATextInfo(boolean lafCondition, + Map map) { + SunToolkit.setAAFontSettingsCondition(lafCondition); + Toolkit tk = Toolkit.getDefaultToolkit(); + Object desktopHints = tk.getDesktopProperty(SunToolkit.DESKTOPFONTHINTS); - @SuppressWarnings("unchecked") - public static AATextInfo getAATextInfo(boolean lafCondition) { - SunToolkit.setAAFontSettingsCondition(lafCondition); - Toolkit tk = Toolkit.getDefaultToolkit(); - Object map = tk.getDesktopProperty(SunToolkit.DESKTOPFONTHINTS); - if (map instanceof Map) { - return getAATextInfoFromMap((Map)map); - } else { - return null; + if (desktopHints instanceof Map) { + Map hints = (Map) desktopHints; + Object aaHint = hints.get(KEY_TEXT_ANTIALIASING); + if (aaHint == null + || aaHint == VALUE_TEXT_ANTIALIAS_OFF + || aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) { + return; } - } - - Object aaHint; - Integer lcdContrastHint; - FontRenderContext frc; - - /* These are rarely constructed objects, and only when a complete - * UI is being updated, so the cost of the tests here is minimal - * and saves tests elsewhere. - * We test that the values are ones we support/expect. - */ - public AATextInfo(Object aaHint, Integer lcdContrastHint) { - if (aaHint == null) { - throw new InternalError("null not allowed here"); - } - if (aaHint == VALUE_TEXT_ANTIALIAS_OFF || - aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) { - throw new InternalError("AA must be on"); - } - this.aaHint = aaHint; - this.lcdContrastHint = lcdContrastHint; - this.frc = new FontRenderContext(null, aaHint, - VALUE_FRACTIONALMETRICS_DEFAULT); + map.put(KEY_TEXT_ANTIALIASING, aaHint); + map.put(KEY_TEXT_LCD_CONTRAST, hints.get(KEY_TEXT_LCD_CONTRAST)); } } @@ -241,22 +198,6 @@ // /** - * Returns whether or not text should be drawn antialiased. - * - * @param c JComponent to test. - * @return Whether or not text should be drawn antialiased for the - * specified component. - */ - public static AATextInfo drawTextAntialiased(JComponent c) { - if (c != null) { - /* a non-null property implies some form of AA requested */ - return (AATextInfo)c.getClientProperty(AA_TEXT_PROPERTY_KEY); - } - // No component, assume aa is off - return null; - } - - /** * Returns the left side bearing of the first character of string. The * left side bearing is calculated from the passed in * FontMetrics. If the passed in String is less than one @@ -530,7 +471,6 @@ // If we get here we're not printing if (g instanceof Graphics2D) { - AATextInfo info = drawTextAntialiased(c); Graphics2D g2 = (Graphics2D)g; boolean needsTextLayout = ((c != null) && @@ -543,21 +483,25 @@ } } - if (info != null) { + Object aaHint = c.getClientProperty(KEY_TEXT_ANTIALIASING); + if (aaHint != null) { Object oldContrast = null; Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING); - if (info.aaHint != oldAAValue) { - g2.setRenderingHint(KEY_TEXT_ANTIALIASING, info.aaHint); + if (aaHint != oldAAValue) { + g2.setRenderingHint(KEY_TEXT_ANTIALIASING, aaHint); } else { oldAAValue = null; } - if (info.lcdContrastHint != null) { + + Object lcdContrastHint = c.getClientProperty( + KEY_TEXT_LCD_CONTRAST); + if (lcdContrastHint != null) { oldContrast = g2.getRenderingHint(KEY_TEXT_LCD_CONTRAST); - if (info.lcdContrastHint.equals(oldContrast)) { + if (lcdContrastHint.equals(oldContrast)) { oldContrast = null; } else { g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, - info.lcdContrastHint); + lcdContrastHint); } } @@ -821,24 +765,26 @@ } // Assume we're not printing if we get here, or that we are invoked // via Swing text printing which is laid out for the printer. - AATextInfo info = drawTextAntialiased(c); - if (info != null && (g instanceof Graphics2D)) { + Object aaHint = c.getClientProperty(KEY_TEXT_ANTIALIASING); + if (aaHint != null && (g instanceof Graphics2D)) { Graphics2D g2 = (Graphics2D)g; Object oldContrast = null; Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING); - if (info.aaHint != null && info.aaHint != oldAAValue) { - g2.setRenderingHint(KEY_TEXT_ANTIALIASING, info.aaHint); + if (aaHint != null && aaHint != oldAAValue) { + g2.setRenderingHint(KEY_TEXT_ANTIALIASING, aaHint); } else { oldAAValue = null; } - if (info.lcdContrastHint != null) { + + Object lcdContrastHint = c.getClientProperty(KEY_TEXT_LCD_CONTRAST); + if (lcdContrastHint != null) { oldContrast = g2.getRenderingHint(KEY_TEXT_LCD_CONTRAST); - if (info.lcdContrastHint.equals(oldContrast)) { + if (lcdContrastHint.equals(oldContrast)) { oldContrast = null; } else { g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, - info.lcdContrastHint); + lcdContrastHint); } } @@ -1117,15 +1063,35 @@ */ private static FontRenderContext getFRCProperty(JComponent c) { if (c != null) { - AATextInfo info = - (AATextInfo)c.getClientProperty(AA_TEXT_PROPERTY_KEY); - if (info != null) { - return info.frc; + Object aaHint = c.getClientProperty(KEY_TEXT_ANTIALIASING); + if (aaHint != null) { + return getFRCFromCache(aaHint); } } return null; } + private static final Object APP_CONTEXT_FRC_CACHE_KEY = new Object(); + + private static FontRenderContext getFRCFromCache(Object aaHint) { + @SuppressWarnings("unchecked") + Map cache = (Map) + AppContext.getAppContext().get(APP_CONTEXT_FRC_CACHE_KEY); + + if (cache == null) { + cache = new HashMap<>(); + AppContext.getAppContext().put(APP_CONTEXT_FRC_CACHE_KEY, cache); + } + + FontRenderContext frc = cache.get(aaHint); + if (frc == null) { + frc = new FontRenderContext(null, aaHint, + VALUE_FRACTIONALMETRICS_DEFAULT); + cache.put(aaHint, frc); + } + return frc; + } + /* * returns true if the Graphics is print Graphics * false otherwise diff -r e07799997e1f -r e00f713e6103 jdk/test/javax/swing/UIDefaults/6302464/bug6302464.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/swing/UIDefaults/6302464/bug6302464.java Mon Aug 24 16:06:36 2015 +0400 @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2015, 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. + * + * 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. + */ + +import java.util.Map; +import java.util.HashSet; +import java.awt.Color; +import java.awt.Toolkit; +import java.awt.Graphics2D; +import java.awt.font.FontRenderContext; +import java.awt.image.BufferedImage; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UIDefaults; +import javax.swing.UIManager.LookAndFeelInfo; +import javax.swing.plaf.basic.BasicLookAndFeel; +import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING; +import static java.awt.RenderingHints.KEY_TEXT_LCD_CONTRAST; +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_GASP; +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR; +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB; +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR; +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB; +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_OFF; + +/** + * @test + * @bug 6302464 + * @author Alexandr Scherbatiy + * @summary Allow programmatic enabling of subpixel anti-aliasing in Swing + */ +public class bug6302464 { + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(bug6302464::testAntialiasingProperties); + } + + private static void testAntialiasingProperties() { + testCustomLAF(); + testFontRenderingContext(); + testAntialiasingHints(); + testLAFAAHints(); + } + + private static void testCustomLAF() { + try { + testCustomLAF(false); + testCustomLAF(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void testCustomLAF(boolean useAAHints) throws Exception { + CustomLookAndFeel customLAF = new CustomLookAndFeel(useAAHints); + UIManager.setLookAndFeel(customLAF); + + JLabel label = new JLabel(); + Object aaHint = label.getClientProperty(KEY_TEXT_ANTIALIASING); + Object lcdContrastHint = label.getClientProperty(KEY_TEXT_LCD_CONTRAST); + + if (aaHint != customLAF.getAAHint()) { + throw new RuntimeException("AA hint from custom L&F is not set"); + } + + if (lcdContrastHint != customLAF.getLCDContarstHint()) { + throw new RuntimeException("AA hint from custom L&F is not set"); + } + } + + private static final Object[] ANTIALIASING_HINTS = { + VALUE_TEXT_ANTIALIAS_GASP, + VALUE_TEXT_ANTIALIAS_LCD_HRGB, + VALUE_TEXT_ANTIALIAS_LCD_HBGR, + VALUE_TEXT_ANTIALIAS_LCD_VRGB, + VALUE_TEXT_ANTIALIAS_LCD_VBGR + }; + + private static void testFontRenderingContext() { + for (Object aaHint : ANTIALIASING_HINTS) { + testFontRenderingContext(aaHint); + } + } + + private static void testFontRenderingContext(Object aaHint) { + + JLabel label = new JLabel("Test"); + label.putClientProperty(KEY_TEXT_ANTIALIASING, aaHint); + FontRenderContext frc = label.getFontMetrics( + label.getFont()).getFontRenderContext(); + + if (!aaHint.equals(frc.getAntiAliasingHint())) { + throw new RuntimeException("Wrong aa hint in FontRenderContext"); + } + } + + private static void testAntialiasingHints() { + setMetalLookAndFeel(); + + HashSet colorsAAOff = getAntialiasedColors(VALUE_TEXT_ANTIALIAS_OFF, 100); + + if (colorsAAOff.size() > 2) { + throw new RuntimeException("Wrong number of antialiased colors."); + } + + HashSet colorsAAOnLCD100 = getAntialiasedColors( + VALUE_TEXT_ANTIALIAS_LCD_HRGB, 100); + + if (colorsAAOnLCD100.size() <= 2) { + throw new RuntimeException("Wrong number of antialiased colors."); + } + + HashSet colorsAAOnLCD250 = getAntialiasedColors( + VALUE_TEXT_ANTIALIAS_LCD_HRGB, 250); + + if (colorsAAOnLCD250.size() <= 2) { + throw new RuntimeException("Wrong number of antialiased colors."); + } + + if (colorsAAOnLCD100.equals(colorsAAOnLCD250)) { + throw new RuntimeException("LCD contarst is not used."); + } + } + + private static HashSet getAntialiasedColors(Object aaHint, int lcdContrast) { + + JLabel label = new JLabel("ABCD"); + label.setSize(label.getPreferredSize()); + label.putClientProperty(KEY_TEXT_ANTIALIASING, aaHint); + label.putClientProperty(KEY_TEXT_LCD_CONTRAST, lcdContrast); + + int w = label.getWidth(); + int h = label.getHeight(); + + BufferedImage buffImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + Graphics2D g = buffImage.createGraphics(); + g.setColor(Color.WHITE); + g.fillRect(0, 0, w, h); + label.paint(g); + g.dispose(); + + HashSet colors = new HashSet<>(); + + for (int i = 0; i < w; i++) { + for (int j = 0; j < h; j++) { + Color color = new Color(buffImage.getRGB(i, j)); + colors.add(color); + } + } + + return colors; + } + + private static void setMetalLookAndFeel() { + setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + } + + private static void setLookAndFeel(String lafClass) { + try { + UIManager.setLookAndFeel(lafClass); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void testLAFAAHints() { + + for (LookAndFeelInfo lafInfo : UIManager.getInstalledLookAndFeels()) { + testLAFAAHints(lafInfo); + } + } + + private static final String[] EXCLUDED_LAFS = {"CDE/Motif"}; + + private static boolean isExcludedLAF(LookAndFeelInfo lafInfo) { + for (String excludedLaf : EXCLUDED_LAFS) { + if (lafInfo.getName().equals(excludedLaf)) { + return true; + } + } + return false; + } + + private static void testLAFAAHints(LookAndFeelInfo lafInfo) { + setLookAndFeel(lafInfo.getClassName()); + + Object uiAAHint = UIManager.getDefaults().get(KEY_TEXT_ANTIALIASING); + Object uiLCDContrastHint = UIManager.getDefaults().get( + KEY_TEXT_LCD_CONTRAST); + + Object aaHints = Toolkit.getDefaultToolkit(). + getDesktopProperty("awt.font.desktophints"); + + if (isExcludedLAF(lafInfo)) { + if (uiAAHint != null || uiLCDContrastHint != null) { + throw new RuntimeException("Rendering hints set for excluded L&F"); + } + } else if (aaHints instanceof Map) { + Map map = (Map) aaHints; + + if (uiAAHint != map.get(KEY_TEXT_ANTIALIASING)) { + throw new RuntimeException("UI defaults contains wrong aa hint"); + } + + if (uiLCDContrastHint != map.get(KEY_TEXT_LCD_CONTRAST)) { + throw new RuntimeException("UI defaults contains wrong" + + "lcd contrast hint"); + } + } else if (uiAAHint != null || uiLCDContrastHint != null) { + throw new RuntimeException("Rendering hints set for empty desktop" + + "properties"); + } + } + + private static class CustomLookAndFeel extends BasicLookAndFeel { + + private final boolean useAAHints; + + public CustomLookAndFeel(boolean useAAHints) { + this.useAAHints = useAAHints; + } + + @Override + public String getDescription() { + return getName(); + } + + @Override + public String getName() { + return "Custom L&F"; + } + + @Override + public String getID() { + return getName(); + } + + @Override + public boolean isNativeLookAndFeel() { + return false; + } + + @Override + public boolean isSupportedLookAndFeel() { + return true; + } + + @Override + protected void initClassDefaults(UIDefaults table) { + super.initClassDefaults(table); + table.put(KEY_TEXT_ANTIALIASING, getAAHint()); + table.put(KEY_TEXT_LCD_CONTRAST, getLCDContarstHint()); + } + + private Object getAAHint() { + return useAAHints ? VALUE_TEXT_ANTIALIAS_GASP : null; + } + + private Object getLCDContarstHint() { + return useAAHints ? 115 : null; + } + } +}