有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java2D/Swing:将具有文本抗锯齿的组件呈现到BuffereImage

我想将一个JavaSwing组件,例如一个JButton,也放在JFrame上,呈现到一个BuffereImage。这通常是可行的,但有一个主要缺点:文本抗锯齿,尤其是“LCD”抗锯齿模式,在渲染到BuffereImage时不起作用

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

  • 操作系统: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”(BuffereImage的Graphics2D上下文)上明确设置文本抗锯齿的渲染提示,如下所示:

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

但这根本没有效果

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

我真的很感激任何帮助-非常感谢


共 (3) 个答案

  1. # 1 楼答案

    您希望(a)半透明的BuffereImage与JButton外观一样好,或者(b)希望JButton和BuffereImage外观相同

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

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

  2. # 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

  3. # 3 楼答案

    这是一个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 += A*CD = D*(1-A)+A*C或其他合适的混合函数的形式发送到目标图像