Java2D / Swing:将具有文本抗锯齿功能的组件渲染到BufferedImage

时间:2011-11-03 09:00:00

标签: java bufferedimage java-2d antialiasing graphics2d

我想渲染一个Java Swing组件,例如一个JButton,我也把它放在一个JFrame上,一个BufferedImage。这通常有效,但有一个主要缺点:文本抗锯齿,尤其是“LCD”抗锯齿模式,在渲染到BufferedImage时无效。

我已经将一些示例代码放在一起来演示问题,但首先是我的系统信息:

  • 操作系统:Windows 7 64位
  • JVM :1.6.0_26-b03(32位)

以下示例代码将创建一个简单的JFrame,在其上放置一个JButton,然后将JButton呈现为文件“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();
  }
}

下图显示了屏幕上JFrame中的JButton与图像文件中相同的呈现JButton之间的区别:

enter image description here

实际上图像中有一些文本抗锯齿,但不是JFrame屏幕上显示的LCD优化抗锯齿(此问题也出现在默认的LookAndFeel中,而不仅仅出现在“WindowsLookAndFeel”中)。

我已经尝试在“g2d”(BufferedImage的Graphics2D上下文)中明确设置RenderingHint for text anti aliasing,如下所示:

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

但它根本没有效果。

我迫切需要将JButton呈现为一个图像文件,就像它在屏幕上呈现一样(这只是一个例子,实际上我需要渲染一些更复杂的组件,这些组件都遭受了这种抗锯齿问题)我希望那里是一个真正的解决方案,没有任何讨厌的解决方法,如截取屏幕截图等。

非常感谢任何帮助 - 非常感谢!

3 个答案:

答案 0 :(得分:3)

这是一个JVM错误

我遇到了和你一样的问题。我已经得出结论,问题确实在于绘制到半透明的位图。我很确定我们正在点击:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6749069

解决方法

现在我绘制一个不透明的位图并将其blit到我的目标位图中。如果文本背后的背景颜色是明白的,这应该有效:

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);
}

如果您的背景不是纯色,则必须将文本在黑色上呈现为位图A。然后使用文本颜色C填充另一个位图。然后将其显示到目标图片{},DD += A*CD = D*(1-A)+A*C或其他合适的混合功能。

答案 1 :(得分:2)

反正可能已经太晚了。问题可能与您正在使用的半透明BufferedImage图片有关。以下似乎工作正常:

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);

环顾四周时,我发现了类似的complains

答案 2 :(得分:0)

要么(a)您的半透明BufferedImage看起来和您的JButton一样好,要么(b)您希望您的JButton和BufferedImage看起来一样。

如果(b),你能有半透明的JButton吗?它看起来和你的半透明BufferedImage一样吗?

如果(a)然后很难做到这一点,你可以将JButton绘制成一个非半透明的BufferedImage(如果Java有一个灰度BufferedImage,请使用它)。这将最终成为最终BufferedImage的alpha通道的负面(黑色像素变为完全不透明,白色完全透明。)要获得BufferedImage中像素的颜色,您需要设置任何不透明的像素黑色 - 像素的透明度应该使它成为你想要的灰色。