12

When I run my swing GUI applications under Java 6, they automatically use my configured sub-pixel anti-alias settings for all fonts. The result is much improved over standard AA options.

But when I paint to an image I can find no way to initialize the graphics context to use the system's AA configuration. Trying to play around with Java's different AA hints is a lost cause because no sub-pixel method will work for all users.

Is there any way to inherit system AA settings for a given graphics context instead of having to pick one and explicitly set the hint? At the moment I have to use GASP AA to avoid the horrible results that standard AA gives with small fonts. I have tried not setting anything for text AA, and not setting any AA hints at all.


Update 2010-01-05

I think I have pinned this down; the subpixel AA hints appear to only be respected when painting directly to the AWT graphics context; when I paint to a double-buffer image it just does standard AA; but when I bypass the double-buffer image the subpixel AA is done.

Otherwise The_Fire's answer would work in JVMs which have Swing available (but not J2ME JVMs); Note that The_Fire's answer does not work using an AWT component (using new Label() instead of new JLabel() fails), presumably because the FontRenderContext cannot be extracted until the component is realized to the display.


My current code to get the graphics context for my target image currently looks like this:

try {
    if((dbImage=dctRoot.createImage(wid,hgt,1))!=null) {            // if createImage returns null or throws an exception the component is not yet displayable
        dbGraphics=(Graphics2D)dbImage.getGraphics();
        if(dctRoot.properties.getBoolean("Antialias",true)) {
            try {
                // set AA on overall
                dbGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING     ,RenderingHints.VALUE_ANTIALIAS_ON);
                // set text AA to platform/impl default
                dbGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
                // try to override platform/impl AA with font-specified AA (Java 6+)
                try { dbGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_GASP").get(null)); } catch(Throwable thr) {;} // yes, ignore exception
                }
            catch(Throwable thr) {
                dctRoot.log.println("Antialiasing not supported on this JVM ("+thr+").");
                dctRoot.setProperty("Antialias","False");           // turn off AA for subsequent painting
                }
            }
        }
    }
catch(Throwable thr) {
    dbImage=null;
    dbGraphics=null;
    }

The code to create the image uses an underlying AWT component, which forms the backdrop on which I do all my painting - the component is a Panel, because I need to be able to do a setFocusCycleRoot so it plays well with other AWT components. The create image code follows:

public DctImage createImage(int wid, int hgt, float accpty) {
    GraphicsConfiguration               cfg=awtComponent.getGraphicsConfiguration();
    Image                               img=null;

    if(transparentImages) {
        //y { img=new BufferedImage(wid,hgt,BufferedImage.TYPE_INT_ARGB); }     // NB: J2ME CDC/PP 1.1 does not have the BufferedImage constructors (one day I may discover a way to create a BufferedImage via another API!!)
        try { img=cfg.createCompatibleImage(wid,hgt,Transparency.TRANSLUCENT); }// NB: J2ME CDC/PP 1.1 does not have this API, but prefer to use GraphicsConfiguration over new BufferImage(...)
        catch(NoClassDefFoundError   thr) { transparentImages=false; createImage(wid,hgt,accpty); } // try again with transparency disabled
        catch(NoSuchMethodError      thr) { transparentImages=false; createImage(wid,hgt,accpty); } // try again with transparency disabled
        catch(NoSuchFieldError       thr) { transparentImages=false; createImage(wid,hgt,accpty); } // try again with transparency disabled
        }
    else {
        img=cfg.createCompatibleImage(wid,hgt);
        }

    if(accpty>0 && SET_ACCELERATION_PRIORITY!=null) {
        try { SET_ACCELERATION_PRIORITY.invoke(img,new Object[]{new Float(accpty)}); } catch(Throwable thr) {;}
        }

    return (img==null ? null : new DctImage(img));
    }
Lawrence Dol
  • 63,018
  • 25
  • 139
  • 189
  • Not to do with on- or off- screen (so this is a comment, not an answer), but you are aware of how certain foreign characters in a string cause swing to switch back to TextLayout font rendering (which is not anti-aliased), right? – whybird Dec 15 '09 at 23:52

4 Answers4

7

I found there were a few factors going on here.

First, the image needs to be created from the underlying AWT component, and it must be created without transparency:

cfg.createCompatibleImage(wid,hgt);

instead of

cfg.createCompatibleImage(wid,hgt,Transparency.TRANSLUCENT);

Second, for some inexplicable reason, the primary AA setting, KEY_ANTIALIASING, must be off to use LCD subpixel AA.

Lastly, and most importantly, the desktop font rendering hints are easily retrieved using:

java.awt.Toolkit.getDesktopProperty("awt.font.desktophints")

Update 2010-01-05

Retesting in Java 6.26, it seems like the problem with needing to set general AA off to render text AA has finally been resolved (way to go Oracle... after Sun was just a few years decade too late).

Lawrence Dol
  • 63,018
  • 25
  • 139
  • 189
6

Using Swing, I'm able to get the right text anti-aliasing hint like this:

JLabel label = new JLabel();
FontMetrics fm  = label.getFontMetrics( label.getFont() );
Object aaHintValue = fm.getFontRenderContext().getAntiAliasingHint();

On my system this returns RenderingHits.VALUE_TEXT_ANTIALIAS_LCD_HRGB.

The_Fire
  • 101
  • 1
  • That's an interesting thought. I can't use Swing - not supported on J2ME/CDC/PP - but I might be able to get the same out of an AWT window. – Lawrence Dol Dec 23 '09 at 18:16
  • I need to test this at work... but since getFontMetrics() is a Component method, I fully expect this to work in my context. Assuming it does, you can expect an accept on this in Jan. – Lawrence Dol Dec 24 '09 at 07:33
  • Giving this a +1 because it does work to get the correct AA hint; but it doesn't work for AWT (presumably because the native component is not realized), and even applying the specified LCD hint does not do subpixel AA, which is what I need for my problem to be resolved. – Lawrence Dol Jan 06 '10 at 00:41
  • Obviously a few years have passed and so the Java you're running is probably different from the one I'm running, but on my system (Java 7, Mac OS 10.9), that code returns VALUE_TEXT_ANTIALIAS_ON - which is indeed what Java is using, even though the OS itself is using sub-pixel rendering. – Hakanai Dec 11 '13 at 02:59
  • @Trejkaz: You missed, I suspect, the part about needing it to work with pure AWT, not using Swing. The above code does (and did) work, but not without Swing. – Lawrence Dol Jul 09 '14 at 16:25
  • @LawrenceDol huh? I was just pointing out that the behaviour has changed. I didn't say anything about it working or not working. – Hakanai Jul 14 '14 at 06:51
  • @Trejkaz: OK - I misunderstood the thrust of your comment then; sorry. – Lawrence Dol Jul 14 '14 at 17:20
2

java.awt.Toolkit.getDesktopProperty("awt.font.desktophints") appears to be null on linux, at least without any special vm command line options, presumably because it's unable to figure out what the platform defaults are.. adding e.g "-Dawt.useSystemAAFontSettings=lcd" seems to cure it and enables subpixel rendering if you set the hints on your Graphics2D instance.

1

Wait, are you running this code on a Windows JVM? I thought ClearType was a Microsoft technology that Swing inherits through some native code (ie, not available on Linux or other non Microsoft platforms).

I once wrote a servlet that generated JPGs with anti aliased fonts that ran on Debian, and this was the code I used

Font font = new Font("Komix", Font.PLAIN, 8);
Graphics2D g2;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FontRenderContext  frc = g2.getFontRenderContext();
g2.setFont(font);
g2.setPaint(Color.black);
g2.drawString(sMessage, xOffset, yOffset);

Offhand I can't recall if any of this code relies on Swing (I imported javax.swing and the servlet is about 300 lines long, so I may have thought I needed it for something else), a quick check on Google looks like this is squarely in the AWT space. Hope that helps.

Jason Sperske
  • 29,816
  • 8
  • 73
  • 124
  • Yeah, I can turn AA on easily enough, but it uses standard AA which looks like crap at any font size less than about 20 pt; using GASP AA works better, but tends to just turn AA off for smaller font sizes. Using subpixel AA is best, but you must match the hardware pixel arrangement (vertical or horizontal and RGB or BGR). I would like to inherit whatever AA setting the O/S is using, since that will either be correct, or at least equal to what the user is seeing on their desktop in general. The real problem is that standard AA looks like crap next to subpixel AA. – Lawrence Dol Dec 24 '09 at 07:25
  • BTW, while ClearType is a trademark of Microsoft, the technology involved is *sub-pixel* anti-aliasing, which is available on all major platforms. – Lawrence Dol Nov 08 '10 at 01:48