avoid painting text with our rendering hints enabled to avoid antialiased text in some components if text antialiasing is disabled in system (issue #227)

This commit is contained in:
Karl Tauber
2020-12-18 12:17:43 +01:00
parent 460f0d9dee
commit 956001dbd7
13 changed files with 237 additions and 45 deletions

View File

@@ -27,6 +27,10 @@ FlatLaf Change Log
#224)
- SwingX: Fixed striping background highlighting color (e.g. alternating table
rows) in dark themes.
- Fixed: If text antialiasing is disabled (in OS system settings or via
`-Dawt.useSystemAAFontSettings=off`), then some components still did use
antialiasing to render text (not-editable ComboBox, ProgressBar, Slider,
TabbedPane and multiline ToolTip). (issue #227)
## 0.45

View File

@@ -352,7 +352,7 @@ public class FlatComboBoxUI
FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
int width = c.getWidth();
int height = c.getHeight();
@@ -386,6 +386,9 @@ public class FlatComboBoxUI
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
}
// avoid that the "current value" renderer is invoked with enabled antialiasing
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
paint( g, c );
}

View File

@@ -155,7 +155,7 @@ public class FlatProgressBarUI
? 0
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
FlatUIUtils.setRenderingHints( (Graphics2D) g );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
// paint track
RoundRectangle2D.Float trackShape = new RoundRectangle2D.Float( x, y, width, height, arc, arc );
@@ -163,6 +163,7 @@ public class FlatProgressBarUI
((Graphics2D)g).fill( trackShape );
// paint progress
int amountFull = 0;
if( progressBar.isIndeterminate() ) {
boxRect = getBox( boxRect );
if( boxRect != null ) {
@@ -170,11 +171,8 @@ public class FlatProgressBarUI
((Graphics2D)g).fill( new RoundRectangle2D.Float( boxRect.x, boxRect.y,
boxRect.width, boxRect.height, arc, arc ) );
}
if( progressBar.isStringPainted() )
paintString( g, x, y, width, height, 0, insets );
} else {
int amountFull = getAmountFull( insets, width, height );
amountFull = getAmountFull( insets, width, height );
RoundRectangle2D.Float progressShape = horizontal
? new RoundRectangle2D.Float( c.getComponentOrientation().isLeftToRight() ? x : x + (width - amountFull),
@@ -189,10 +187,12 @@ public class FlatProgressBarUI
((Graphics2D)g).fill( area );
} else
((Graphics2D)g).fill( progressShape );
if( progressBar.isStringPainted() )
paintString( g, x, y, width, height, amountFull, insets );
}
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
if( progressBar.isStringPainted() )
paintString( g, x, y, width, height, amountFull, insets );
}
@Override

View File

@@ -94,6 +94,8 @@ public class FlatSliderUI
protected boolean thumbHover;
protected boolean thumbPressed;
private Object[] oldRenderingHints;
public static ComponentUI createUI( JComponent c ) {
return new FlatSliderUI();
}
@@ -211,7 +213,7 @@ public class FlatSliderUI
@Override
public void paint( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
oldRenderingHints = FlatUIUtils.setRenderingHints( g );
/*debug
g.setColor( Color.gray );
@@ -227,6 +229,16 @@ public class FlatSliderUI
debug*/
super.paint( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
oldRenderingHints = null;
}
@Override
public void paintLabels( Graphics g ) {
FlatUIUtils.runWithoutRenderingHints( g, oldRenderingHints, () -> {
super.paintLabels( g );
} );
}
@Override

View File

@@ -226,6 +226,8 @@ public class FlatTabbedPaneUI
private boolean rolloverTabClose;
private boolean pressedTabClose;
private Object[] oldRenderingHints;
public static ComponentUI createUI( JComponent c ) {
return new FlatTabbedPaneUI();
}
@@ -791,9 +793,12 @@ public class FlatTabbedPaneUI
@Override
public void update( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
oldRenderingHints = FlatUIUtils.setRenderingHints( g );
super.update( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
oldRenderingHints = null;
}
@Override
@@ -874,27 +879,29 @@ public class FlatTabbedPaneUI
{
g.setFont( font );
// html
View view = getTextViewForTab( tabIndex );
if( view != null ) {
view.paint( g, textRect );
return;
}
FlatUIUtils.runWithoutRenderingHints( g, oldRenderingHints, () -> {
// html
View view = getTextViewForTab( tabIndex );
if( view != null ) {
view.paint( g, textRect );
return;
}
// plain text
Color color;
if( tabPane.isEnabled() && tabPane.isEnabledAt( tabIndex ) ) {
color = tabPane.getForegroundAt( tabIndex );
if( isSelected && (color instanceof UIResource) && selectedForeground != null )
color = selectedForeground;
} else
color = disabledForeground;
// plain text
Color color;
if( tabPane.isEnabled() && tabPane.isEnabledAt( tabIndex ) ) {
color = tabPane.getForegroundAt( tabIndex );
if( isSelected && (color instanceof UIResource) && selectedForeground != null )
color = selectedForeground;
} else
color = disabledForeground;
int mnemIndex = FlatLaf.isShowMnemonics() ? tabPane.getDisplayedMnemonicIndexAt( tabIndex ) : -1;
int mnemIndex = FlatLaf.isShowMnemonics() ? tabPane.getDisplayedMnemonicIndexAt( tabIndex ) : -1;
g.setColor( color );
FlatUIUtils.drawStringUnderlineCharAt( tabPane, g, title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent() );
g.setColor( color );
FlatUIUtils.drawStringUnderlineCharAt( tabPane, g, title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent() );
} );
}
@Override

View File

@@ -116,7 +116,6 @@ public class FlatToolTipUI
FontMetrics fm = c.getFontMetrics( c.getFont() );
Insets insets = c.getInsets();
FlatUIUtils.setRenderingHints( (Graphics2D) g );
g.setColor( c.getForeground() );
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );

View File

@@ -240,10 +240,57 @@ public class FlatUIUtils
/**
* Sets rendering hints used for painting.
*/
public static void setRenderingHints( Graphics2D g ) {
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL,
public static Object[] setRenderingHints( Graphics g ) {
Graphics2D g2 = (Graphics2D) g;
Object[] oldRenderingHints = new Object[] {
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
};
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL,
MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE );
return oldRenderingHints;
}
/**
* Resets rendering hints previously set with {@link #setRenderingHints(Graphics2D)}.
*/
public static void resetRenderingHints( Graphics g, Object[] oldRenderingHints ) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldRenderingHints[0] );
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, oldRenderingHints[1] );
}
/**
* Temporary resets rendering hints set with {@link #setRenderingHints(Graphics2D)}
* and runs the given runnable.
* <p>
* This is intended for painting text while rendering hints are set.
* <p>
* If text antialiasing is disabled (in OS system settings or via
* {@code -Dawt.useSystemAAFontSettings=off}), but general antialiasing is enabled,
* then text is still painted using some kind of "grayscale" antialiasing,
* which may make the text look bold (depends on font and font size).
* To avoid this, temporary disable general antialiasing.
* This does not affect text rendering if text antialiasing is enabled (usually the default).
*/
public static void runWithoutRenderingHints( Graphics g, Object[] oldRenderingHints, Runnable runnable ) {
if( oldRenderingHints == null ) {
runnable.run();
return;
}
Graphics2D g2 = (Graphics2D) g;
Object[] oldRenderingHints2 = new Object[] {
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
};
resetRenderingHints( g2, oldRenderingHints );
runnable.run();
resetRenderingHints( g2, oldRenderingHints2 );
}
public static Color deriveColor( Color color, Color baseColor ) {

View File

@@ -21,6 +21,7 @@ import static com.formdev.flatlaf.FlatClientProperties.clientPropertyBoolean;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
@@ -62,6 +63,8 @@ public class FlatJideTabbedPaneUI
protected boolean hasFullBorder;
protected boolean tabsOverlapBorder;
private Object[] oldRenderingHints;
public static ComponentUI createUI( JComponent c ) {
return new FlatJideTabbedPaneUI();
}
@@ -193,9 +196,12 @@ public class FlatJideTabbedPaneUI
@Override
public void update( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
oldRenderingHints = FlatUIUtils.setRenderingHints( g );
super.update( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
oldRenderingHints = null;
}
@Override
@@ -222,6 +228,15 @@ public class FlatJideTabbedPaneUI
g.fillRect( x, y, w, h );
}
@Override
protected void paintText( Graphics g, int tabPlacement, Font font, FontMetrics metrics,
int tabIndex, String title, Rectangle textRect, boolean isSelected )
{
FlatUIUtils.runWithoutRenderingHints( g, oldRenderingHints, () -> {
super.paintText( g, tabPlacement, font, metrics, tabIndex, title, textRect, isSelected );
} );
}
@Override
protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h,
boolean isSelected )

View File

@@ -60,6 +60,8 @@ public class FlatRangeSliderUI
protected Color disabledThumbColor;
protected Color disabledThumbBorderColor;
private Object[] oldRenderingHints;
public static ComponentUI createUI( JComponent c ) {
return new FlatRangeSliderUI();
}
@@ -190,7 +192,7 @@ public class FlatRangeSliderUI
@Override
public void paint( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
oldRenderingHints = FlatUIUtils.setRenderingHints( g );
/*debug
g.setColor( Color.gray );
@@ -209,6 +211,16 @@ public class FlatRangeSliderUI
debug*/
super.paint( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
oldRenderingHints = null;
}
@Override
public void paintLabels( Graphics g ) {
FlatUIUtils.runWithoutRenderingHints( g, oldRenderingHints, () -> {
super.paintLabels( g );
} );
}
@Override

View File

@@ -60,13 +60,6 @@ public class FlatHyperlinkUI
disabledText = null;
}
@Override
public void paint( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
super.paint( g, c );
}
@Override
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
FlatButtonUI.paintText( g, b, textRect, text, b.isEnabled() ? b.getForeground() : disabledText );
@@ -78,8 +71,12 @@ public class FlatHyperlinkUI
private void paintUnderline( Graphics g, Rectangle rect ) {
int descent = g.getFontMetrics().getDescent();
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
((Graphics2D)g).fill( new Rectangle2D.Float(
rect.x, (rect.y + rect.height) - descent + UIScale.scale( 1f ),
rect.width, UIScale.scale( 1f ) ) );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
}

View File

@@ -21,6 +21,7 @@ import javax.swing.*;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*;
import org.jdesktop.swingx.*;
/**
* @author Karl Tauber
@@ -89,6 +90,12 @@ public class FlatHtmlTest
label14 = new JLabel();
label15 = new JLabel();
label16 = new JLabel();
label17 = new JLabel();
comboBox1 = new JComboBox<>();
comboBox2 = new JComboBox<>();
label18 = new JLabel();
xHyperlink1 = new JXHyperlink();
xHyperlink2 = new JXHyperlink();
label1 = new JLabel();
scrollPane15 = new JScrollPane();
editorPane1 = new JEditorPane();
@@ -150,6 +157,8 @@ public class FlatHtmlTest
"[]" +
"[]" +
"[]" +
"[]" +
"[]" +
"[]"));
//---- label5 ----
@@ -283,6 +292,38 @@ public class FlatHtmlTest
label16.setText("(move mouse here)");
label16.setToolTipText("Some text");
panel1.add(label16, "cell 2 9");
//---- label17 ----
label17.setText("JComboBox:");
panel1.add(label17, "cell 0 10");
//---- comboBox1 ----
comboBox1.setModel(new DefaultComboBoxModel<>(new String[] {
"<html>Some <b>Bold</b> Text",
"abc",
"def"
}));
panel1.add(comboBox1, "cell 1 10");
//---- comboBox2 ----
comboBox2.setModel(new DefaultComboBoxModel<>(new String[] {
"Some Text",
"abc",
"def"
}));
panel1.add(comboBox2, "cell 2 10");
//---- label18 ----
label18.setText("JXHyperlink:");
panel1.add(label18, "cell 0 11");
//---- xHyperlink1 ----
xHyperlink1.setText("<html>Some <b>Bold</b> Text");
panel1.add(xHyperlink1, "cell 1 11");
//---- xHyperlink2 ----
xHyperlink2.setText("Some text");
panel1.add(xHyperlink2, "cell 2 11");
}
add(panel1, "cell 4 0 1 3,aligny top,growy 0");
@@ -400,6 +441,12 @@ public class FlatHtmlTest
private JLabel label14;
private JLabel label15;
private JLabel label16;
private JLabel label17;
private JComboBox<String> comboBox1;
private JComboBox<String> comboBox2;
private JLabel label18;
private JXHyperlink xHyperlink1;
private JXHyperlink xHyperlink2;
private JLabel label1;
private JScrollPane scrollPane15;
private JEditorPane editorPane1;

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.3.1.342" Java: "15" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -36,7 +36,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[fill][fill][fill]"
"$rowConstraints": "[][][][][][][][][][]"
"$rowConstraints": "[][][][][][][][][][][][]"
} ) {
name: "panel1"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -227,6 +227,52 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 9"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label17"
"text": "JComboBox:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 10"
} )
add( new FormComponent( "javax.swing.JComboBox" ) {
name: "comboBox1"
"model": new javax.swing.DefaultComboBoxModel {
selectedItem: "<html>Some <b>Bold</b> Text"
addElement( "<html>Some <b>Bold</b> Text" )
addElement( "abc" )
addElement( "def" )
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 10"
} )
add( new FormComponent( "javax.swing.JComboBox" ) {
name: "comboBox2"
"model": new javax.swing.DefaultComboBoxModel {
selectedItem: "Some Text"
addElement( "Some Text" )
addElement( "abc" )
addElement( "def" )
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 10"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label18"
"text": "JXHyperlink:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 11"
} )
add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) {
name: "xHyperlink1"
"text": "<html>Some <b>Bold</b> Text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 11"
} )
add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) {
name: "xHyperlink2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 11"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 0 1 3,aligny top,growy 0"
} )
@@ -296,7 +342,7 @@ new FormModel {
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 695, 755 )
"size": new java.awt.Dimension( 820, 755 )
} )
}
}

View File

@@ -68,6 +68,9 @@ public class FlatTestFrame
public boolean applyComponentOrientationToFrame;
public static FlatTestFrame create( String[] args, String title ) {
// disable text antialiasing
// System.setProperty( "awt.useSystemAAFontSettings", "off" );
DemoPrefs.init( PREFS_ROOT_PATH );
// set scale factor