Java Font Rendering

Lawrence Dol picture Lawrence Dol · Jan 7, 2010 · Viewed 8.8k times · Source

I have been attempting to enhance my GUI system written in Java to use subpixel antialiasing and have been successful, except for two remaining anomalies. This is a follow on to my other question from a few weeks ago.

The first problem is that setting rendering hints KEY_ANTIALIASING to VALUE_ANTIALIAS_ON causes KEY_TEXT_ANTIALIASING to be ignored when it is set to an LCD (subpixel) AA value. Can anyone shed some light on this? Currently I am forced to VALUE_ANTIALIAS_OFF before rendering text and turn it back on after rendering text (so that other painting, like circles, etc, is AA'd). This problem is proven by the self-contained test program below.

The second problem is that I can find no way to query the underlying O/S setting for AA, so I have to do a rather kludgey workaround, which is to create a Swing JLabel, get it's FontMetrics, get it's FontRenderContext and then get the AA hint from that. Apart from involving Swing in a program that otherwise makes absolutely no use of Swing, it will not work on a device running any J2ME JVM. Can anyone suggest a better way to do this? It's OK if it requires J5 or J6, since the current kludge already requires J6 (but needing only J4 would be best). I have already tried every default setting and using an AWT component instead of JLabel.


Test Program

This program verifies that for subpixel AA to work, the general AA setting must first be disabled. (PS: I write to a back-buffer because my underlying GUI does, and I wanted to test in an equivalent context).

import java.awt.*;
import java.awt.event.*;

public class AwtTestFrame1b extends Panel {

private final Font                      font=new Font(Font.SANS_SERIF, Font.PLAIN, 16);
private final int                       line=25;

AwtTestFrame1b() {
    setBackground(SystemColor.control);
    }

public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D)g;

    int                                 py=0;

    py=paintText(g2d,py,null                                        ,false);
    py=paintText(g2d,py,null                                        ,true );
    py+=line;

    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF     ,false);
    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT ,false);
    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_ON      ,false);
    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_GASP    ,false);
    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB,false);
    py+=line;

    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF     ,true );
    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT ,true );
    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_ON      ,true );
    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_GASP    ,true );
    py=paintText(g2d,py,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB,true );
    py+=line;
    }

private int paintText(Graphics2D g2d, int py, Object val, boolean aa) {
    Graphics2D                          dgc=g2d;
    char[]                              txt=("The quick brown fox jumped over the lazy dog ("+val+", General AA: "+aa+")").toCharArray();
    Image                               img=null;

    GraphicsConfiguration cfg=getGraphicsConfiguration();
    img=cfg.createCompatibleImage(getWidth(),line);
    dgc=(Graphics2D)img.getGraphics();
    dgc.setColor(getBackground());
    dgc.fillRect(0,0,getWidth(),line);
    dgc.setColor(g2d.getColor());

    if(aa       ) { dgc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON ); }
    else          { dgc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF); }
    if(val!=null) { dgc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,val); }
    dgc.setFont(font);

    dgc.drawChars(txt,0,txt.length,10,line-5);
    g2d.drawImage(img,  0,py,  null);

    dgc.dispose();
    img.flush();

    return (py+line);
    }

public static void main(String[] args) {
    Frame                               wnd=new Frame("AWT Antialiased Text Sample");

    wnd.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
            System.exit(0);
            }
        });
    wnd.add(new AwtTestFrame1b());
    wnd.setSize(new Dimension(1000, 600));
    wnd.setVisible(true);
    }

}

Answer

Joshua McKinnon picture Joshua McKinnon · Jan 7, 2010

Are the AWT Desktop Properties of any help? In particular, "awt.font.desktophints" - these contain the AA hints that the native components use, but can be applied to any Graphics2D you want.

Just a shot in the dark, having recently read through the AA section in Filthy Rich Clients.

Use would look something like this:

String str = "A quick brown fox jumps over the lazy dog";
Toolkit tk = Toolkit.getDefaultToolkit();
Map desktopHints = (Map)(tk.getDesktopProperty("awt.font.desktophints"));
Graphics2D g2d = (Graphics2D)g;

if(desktopHints != null) {
    g2d.addRenderingHints(desktopHints);
}

g2d.drawString(str, someX, someY);

I was able to get the same results (using your example class and drawChars and drawImage, just typed drawString for simplicity) as the LCD HRGB mode using these hints and no other calls on my machine.

I'm not sure what release of Java this requires, if it's what you're looking for...