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) #224)
- SwingX: Fixed striping background highlighting color (e.g. alternating table - SwingX: Fixed striping background highlighting color (e.g. alternating table
rows) in dark themes. 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 ## 0.45

View File

@@ -352,7 +352,7 @@ public class FlatComboBoxUI
FlatUIUtils.paintParentBackground( g, c ); FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g; Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
int width = c.getWidth(); int width = c.getWidth();
int height = c.getHeight(); int height = c.getHeight();
@@ -386,6 +386,9 @@ public class FlatComboBoxUI
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) ); 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 ); paint( g, c );
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -240,10 +240,57 @@ public class FlatUIUtils
/** /**
* Sets rendering hints used for painting. * Sets rendering hints used for painting.
*/ */
public static void setRenderingHints( Graphics2D g ) { public static Object[] setRenderingHints( Graphics g ) {
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); Graphics2D g2 = (Graphics2D) g;
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, 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 ); 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 ) { 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 static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
@@ -62,6 +63,8 @@ public class FlatJideTabbedPaneUI
protected boolean hasFullBorder; protected boolean hasFullBorder;
protected boolean tabsOverlapBorder; protected boolean tabsOverlapBorder;
private Object[] oldRenderingHints;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatJideTabbedPaneUI(); return new FlatJideTabbedPaneUI();
} }
@@ -193,9 +196,12 @@ public class FlatJideTabbedPaneUI
@Override @Override
public void update( Graphics g, JComponent c ) { public void update( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g ); oldRenderingHints = FlatUIUtils.setRenderingHints( g );
super.update( g, c ); super.update( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
oldRenderingHints = null;
} }
@Override @Override
@@ -222,6 +228,15 @@ public class FlatJideTabbedPaneUI
g.fillRect( x, y, w, h ); 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 @Override
protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h,
boolean isSelected ) boolean isSelected )

View File

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

View File

@@ -60,13 +60,6 @@ public class FlatHyperlinkUI
disabledText = null; disabledText = null;
} }
@Override
public void paint( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
super.paint( g, c );
}
@Override @Override
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) { protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
FlatButtonUI.paintText( g, b, textRect, text, b.isEnabled() ? b.getForeground() : disabledText ); 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 ) { private void paintUnderline( Graphics g, Rectangle rect ) {
int descent = g.getFontMetrics().getDescent(); int descent = g.getFontMetrics().getDescent();
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
((Graphics2D)g).fill( new Rectangle2D.Float( ((Graphics2D)g).fill( new Rectangle2D.Float(
rect.x, (rect.y + rect.height) - descent + UIScale.scale( 1f ), rect.x, (rect.y + rect.height) - descent + UIScale.scale( 1f ),
rect.width, 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.StringUtils;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*; import net.miginfocom.swing.*;
import org.jdesktop.swingx.*;
/** /**
* @author Karl Tauber * @author Karl Tauber
@@ -89,6 +90,12 @@ public class FlatHtmlTest
label14 = new JLabel(); label14 = new JLabel();
label15 = new JLabel(); label15 = new JLabel();
label16 = 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(); label1 = new JLabel();
scrollPane15 = new JScrollPane(); scrollPane15 = new JScrollPane();
editorPane1 = new JEditorPane(); editorPane1 = new JEditorPane();
@@ -150,6 +157,8 @@ public class FlatHtmlTest
"[]" + "[]" +
"[]" + "[]" +
"[]" + "[]" +
"[]" +
"[]" +
"[]")); "[]"));
//---- label5 ---- //---- label5 ----
@@ -283,6 +292,38 @@ public class FlatHtmlTest
label16.setText("(move mouse here)"); label16.setText("(move mouse here)");
label16.setToolTipText("Some text"); label16.setToolTipText("Some text");
panel1.add(label16, "cell 2 9"); 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"); add(panel1, "cell 4 0 1 3,aligny top,growy 0");
@@ -400,6 +441,12 @@ public class FlatHtmlTest
private JLabel label14; private JLabel label14;
private JLabel label15; private JLabel label15;
private JLabel label16; 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 JLabel label1;
private JScrollPane scrollPane15; private JScrollPane scrollPane15;
private JEditorPane editorPane1; 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 { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -36,7 +36,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3" "$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[fill][fill][fill]" "$columnConstraints": "[fill][fill][fill]"
"$rowConstraints": "[][][][][][][][][][]" "$rowConstraints": "[][][][][][][][][][][][]"
} ) { } ) {
name: "panel1" name: "panel1"
add( new FormComponent( "javax.swing.JLabel" ) { add( new FormComponent( "javax.swing.JLabel" ) {
@@ -227,6 +227,52 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 9" "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 ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 0 1 3,aligny top,growy 0" "value": "cell 4 0 1 3,aligny top,growy 0"
} ) } )
@@ -296,7 +342,7 @@ new FormModel {
} ) } )
}, new FormLayoutConstraints( null ) { }, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 ) "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 boolean applyComponentOrientationToFrame;
public static FlatTestFrame create( String[] args, String title ) { public static FlatTestFrame create( String[] args, String title ) {
// disable text antialiasing
// System.setProperty( "awt.useSystemAAFontSettings", "off" );
DemoPrefs.init( PREFS_ROOT_PATH ); DemoPrefs.init( PREFS_ROOT_PATH );
// set scale factor // set scale factor