6302464: Allow programmatic enabling of subpixel anti-aliasing in Swing on ANY platform
Reviewed-by: serb, azvegint
--- 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) {
--- 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<Object, Object> 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<GTKLookAndFeel> queue = new ReferenceQueue<GTKLookAndFeel>();
--- 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<Object, Object> 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));
}
}
--- 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();
}
}
--- 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) {
--- 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
* <p>
@@ -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));
}
/**
--- 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();
}
--- 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<LookAndFeel> queue = new ReferenceQueue<LookAndFeel>();
@@ -844,8 +840,7 @@
return;
}
- Object aaTextInfo = getAATextInfo();
- defaults.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo);
+ SwingUtilities2.putAATextInfo(useLAFConditions(), defaults);
updateUI();
}
--- 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<java.awt.RenderingHints.Key, Object> 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<Object, Object> 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<java.awt.RenderingHints.Key, Object>)map);
- } else {
- return null;
+ if (desktopHints instanceof Map) {
+ Map<Object, Object> hints = (Map<Object, Object>) 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<Object, FontRenderContext> cache = (Map<Object, FontRenderContext>)
+ 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
--- /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<Color> 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;
+ }
+ }
+}