Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java2D/Swing: Rendering a component with text anti aliasing to a BufferedImage

I would like to render a Java Swing component, e.g. a JButton, which I also put on a JFrame, to a BufferedImage. This works in general, but with a major drawback: Text anti aliasing, especially "LCD" anti aliasing mode, is not working when rendering to a BufferedImage.

I've put some example code together to demonstrate the problem, but first my system information:

  • OS: Windows 7 64 Bit
  • JVM: 1.6.0_26-b03 (32 Bit)

The following example code will create a simple JFrame, put a JButton on it and then renders the JButton to a file "test.png":

public class TextAntiAliasingTest
{
  public TextAntiAliasingTest() throws IOException
  {
    // Create Test-Button which will be rendered to an image
    JButton button = new JButton( "The Test-Button" );
    button.setSize( 200, 70 );
    button.setLocation( 200, 150 );

    // Create JFrame
    final JFrame frame = new JFrame();
    frame.setSize( 800, 600 );
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame.setLayout( null );
    frame.setLocationRelativeTo( null );
    frame.add( button );

    // Show JFrame
    SwingUtilities.invokeLater( new Runnable() {
        @Override public void run() {
            frame.setVisible( true );
        }
    });

    // Render JButton to an BufferedImage
    BufferedImage image = new BufferedImage( 800, 600, BufferedImage.TYPE_INT_ARGB );
    Graphics2D g2d = (Graphics2D)image.getGraphics();
    button.paint( g2d );

    // Write BufferedImage to a PNG file
    ImageIO.write( image, "PNG", new File( "test.png" ) );
  }

  public static void main( String[] args ) throws Exception
  {
    UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
    System.setProperty( "awt.useSystemAAFontSettings", "lcd" );

    new TextAntiAliasingTest();
  }
}

The following image shows the difference between the JButton in the JFrame on screen, and the same rendered JButton in the image file:

enter image description here

Actually there is some text anti aliasing in the image, but not the LCD optimized anti aliasing which is shown on screen in the JFrame (this problem occurs also with the default LookAndFeel, not only with the "WindowsLookAndFeel").

I already tried to explicitely set the RenderingHint for text anti aliasing on the "g2d", the Graphics2D context of the BufferedImage like so:

g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, 
    RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );

But it had no effect at all.

I urgently need to render the JButton to an image file like it is rendered on screen (this is just an example, actually I need to render some more complex components, which all suffer from that anti aliasing problem) and I hope there is a real solution without any nasty workaround like taking a screenshot or something.

I really appreciate any help - thanks a lot!

like image 691
It's Leto Avatar asked Nov 03 '11 09:11

It's Leto


2 Answers

This is a JVM bug

I have encountered the same problem as you. And I have concluded that the problem is indeed with drawing to a translucent bitmap. I'm fairly certain that we're hitting: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6749069

Workaround

For now I draw into an opaque bitmap and blit it into my destination bitmap. If the background color behind your text is plain, this should work:

private void drawString(String text, int x, int y, Graphics2D g, Color bg){    
    // Prepare an off-screen image to draw the string to
    Rectangle2D bounds = g.getFontMetrics().getStringBounds(text, g);
    BufferedImage image = configuration.createCompatibleImage(
                              (int)(bounds.getWidth() + 1.0f), 
                              (int)(bounds.getHeight() + 1.0f),
                              Transparency.OPAQUE);
    Graphics2D ig = image.createGraphics();

    // Fill the background color
    ig.setColor(bg);
    ig.fillRect(0, 0, image.getWidth(), image.getHeight());

    // Draw the string
    int x0 = 0;
    int y0 = ig.getFontMetrics().getAscent();
    ig.setColor(g.getColor());
    ig.setRenderingHints(g.getRenderingHints());
    ig.setFont(g.getFont());
    ig.drawString(text, x0, y0);
    ig.dispose();

    // Blit the image to the destination
    g.drawImage(image, x-x0, y-y0, null);
}

If your background is not a plain color, you'll have to render the text as white on black to a bitmap, A. Then fill another bitmap with your text color C. Then blit that to your destination image, D as: D += A*C or D = D*(1-A)+A*C or some other suitable blending function.

like image 51
Emily L. Avatar answered Oct 03 '22 22:10

Emily L.


It's probably too late anyway. The problem could be related to a translucent BufferedImage image you're using. The following seems to work Ok:

BufferedImage image = new BufferedImage( 800, 600, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D)image.getGraphics();
g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);

While looking around I have found similar complains.

like image 37
tenorsax Avatar answered Oct 03 '22 21:10

tenorsax