【问题标题】:Java2D/Swing: Rendering a component with text anti aliasing to a BufferedImageJava2D/Swing:将带有文本抗锯齿的组件渲染到 BufferedImage
【发布时间】:2011-11-03 09:00:00
【问题描述】:

我想渲染一个 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 之间的区别:

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

我已经尝试为“g2d”上的文本抗锯齿显式设置 RenderingHint,BufferedImage 的 Graphics2D 上下文如下所示:

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

但是一点效果都没有。

我迫切需要将 JButton 渲染为图像文件,就像它在屏幕上渲染一样(这只是一个示例,实际上我需要渲染一些更复杂的组件,它们都存在抗锯齿问题),我希望有是一个真正的解决方案,没有任何讨厌的解决方法,例如截屏或其他东西。

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

【问题讨论】:

  • 为什么不发布SSCCEUIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );,尤其是可以在 Mac 上测试的。或 *nix。
  • 感谢您的评论 - 除了 windows 的外观和感觉之外,还有什么其他东西使我的示例不是 SSCCE?我很乐意发布一个可在 Mac 或 *nix 下测试的示例,但我不明白为什么(除了外观和感觉)它不应该在这些操作系统上运行?
  • 我注意到的第一件事是缺少导入。
  • 您的问题得到解答了吗?

标签: java bufferedimage java-2d antialiasing graphics2d


【解决方案1】:

这是一个 JVM 错误

我遇到了和你一样的问题。我得出的结论是,问题确实出在半透明位图上。我相当肯定我们正在命中:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6749069

解决方法

现在我绘制到一个不透明的位图中,然后将它粘贴到我的目标位图中。如果文本背后的背景颜色是纯色,这应该可以:

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 填充另一个位图。然后将其粘贴到您的目标图像 D 为:D += A*CD = D*(1-A)+A*C 或其他一些合适的混合函数。

【讨论】:

    【解决方案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

    【讨论】:

    • 是的,你当然是对的——当我渲染成半透明的 BufferedImage 时,LCD 抗锯齿会失败;我有这个问题很长时间了(我的目标是淡入一个不是矩形的 JComponent(至少是“渲染的内容”),所以我将它渲染成一个半透明的 BufferedImage 并尝试将它淡入,这基本上是同一个问题),我不知道为什么我在最初的帖子中没有包含这些信息。
    【解决方案3】:

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

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

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

    【讨论】:

    • 欢迎来到 SO,如果可以,请添加一些代码示例来解释您的解决方案。
    猜你喜欢
    • 2011-05-16
    • 1970-01-01
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-29
    • 2012-11-16
    • 1970-01-01
    相关资源
    最近更新 更多