Java2D/Swing:将具有文本抗锯齿功能的组件渲染到 BufferedImage
我想将 Java Swing 组件(例如 JButton)渲染到 BufferedImage,我也将其放在 JFrame 上。这通常有效,但有一个主要缺点:文本抗锯齿,尤其是“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 之间的差异图像文件中的JButton:
实际上图像中有一些文字抗锯齿,但不是LCD优化的JFrame 屏幕上显示的抗锯齿(默认 LookAndFeel 也会出现此问题,而不仅仅是“WindowsLookAndFeel”)。
我已经尝试在“g2d”(BufferedImage 的 Graphics2D 上下文)上显式设置文本抗锯齿的 RenderingHint,如下所示:
g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
但它根本没有效果。
我迫切需要将 JButton 渲染到图像文件,就像它在屏幕上渲染一样(这只是一个例子,实际上我需要渲染一些更复杂的组件,它们都受到抗锯齿问题的困扰),我希望有一个真正的解决方案没有任何令人讨厌的解决方法,例如截屏或其他东西。
我真的很感谢任何帮助 - 非常感谢!
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:
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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是一个 JVM bug
我也遇到过和你一样的问题。我得出的结论是,问题确实在于绘制半透明位图。我相当确定我们正在点击: https://bugs.java.com /bugdatabase/view_bug?bug_id=6749069
解决方法
现在,我绘制一个不透明的位图并将其位块传送到我的目标位图中。如果文本后面的背景颜色是纯色,则这应该可行:
如果您的背景不是纯色,则必须将文本渲染为位图上的黑底白字,
A
。然后用您的文本颜色C
填充另一个位图。然后将其 blit 到您的目标图像D
,如下所示:D += A*C
或D = D*(1-A)+A*C
code> 或其他一些合适的混合函数。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: https://bugs.java.com/bugdatabase/view_bug?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:
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 colorC
. Then blit that to your destination image,D
as:D += A*C
orD = D*(1-A)+A*C
or some other suitable blending function.无论如何,可能已经太晚了。该问题可能与您使用的半透明
BufferedImage
图像有关。以下似乎可行:环顾四周时,我发现了类似的抱怨。
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:While looking around I have found similar complains.
您要么希望 (a) 半透明 BufferedImage 看起来与 JButton 一样好,要么 (b) 您希望 JButton 和 BufferedImage 看起来相同。
如果(b),你能有一个半透明的JButton吗?它看起来和你的半透明 BufferedImage 一样吗?
如果 (a) 那么一个困难的方法是,您可以将 JButton 绘制到非半透明的 BufferedImage(如果 Java 有灰度 BufferedImage,请使用它)。这最终将成为最终 BufferedImage 的 Alpha 通道的负值(黑色像素变得完全不透明,白色像素完全透明。)要获取 BufferedImage 中像素的颜色,您需要设置任何不透明的像素黑色 - 像素的透明度应该使其成为您想要的灰色。
Either you want (a) your translucent BufferedImage to look as good as your JButton, or (b) you want your JButton and BufferedImage to look the same.
If (b), can you have a translucent JButton? Will it look the same as your translucent BufferedImage?
If (a) then a hard way to do it is, you could maybe paint the JButton to a non-translucent BufferedImage (if Java has a greyscale BufferedImage, use this). This will end up being the negative of your final BufferedImage's alpha channel (a black pixel becomes fully opaque, white fully transparent.) To get the colour for the pixels in the BufferedImage, you'll need to set any pixel that isn't transparent to black - the pixel's transparency should make it come out to be the grey you want.