旋转 BufferedImage 时如何产生清晰的绘制结果?
Posted
技术标签:
【中文标题】旋转 BufferedImage 时如何产生清晰的绘制结果?【英文标题】:How can you produce sharp paint results when rotating a BufferedImage? 【发布时间】:2011-10-07 03:00:46 【问题描述】:一种尝试的方法是使用TexturePaint
和g.fillRect()
来绘制图像。但是,这需要您在每次绘制图像时创建一个新的 TexturePaint 和 Rectangle2D 对象,这并不理想 - 无论如何也无济于事。
当我使用g.drawImage(BufferedImage,...)
时,旋转后的图像显得模糊/柔和。
我熟悉 RenderingHints 和双缓冲(我想这就是我正在做的事情),我只是觉得很难相信您不能轻松有效地旋转在 Java 中生成清晰结果的图像。
使用TexturePaint
的代码如下所示。
Grahics2D g2d = (Graphics2D)g;
g2d.setPaint(new TexturePaint(bufferedImage, new Rectangle2D.Float(0,0,50,50)));
g2d.fillRect(0,0,50,50);
我正在使用AffineTransform
将手牌旋转成扇形。
快速绘制漂亮图像的最佳方法是什么?
这是截图:9 很清晰,但其余的牌肯定没有那么清晰。
问题可能出在我创建每个卡片图像并将其存储在数组中时。 这是我目前的做法:
// i from 0 to 52, card codes.
...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage img = gc.createCompatibleImage(86, 126, Transparency.TRANSLUCENT);
Graphics2D g = img.createGraphics();
setRenderingHints(g);
g.drawImage(shadow, 0, 0, 86, 126, null);
g.drawImage(white, 3, 3, 80, 120, null);
g.drawImage(suit, 3, 3, 80, 120, null);
g.drawImage(value, 3, 3, 80, 120, null);
g.dispose();
cardImages[i] = img;
private void setRenderingHints(Graphics2D g)
g.setRenderingHint(KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
我应该如何以不同的方式处理这个问题? 谢谢。
编辑:没有渲染提示
设置 AA 提示没有任何区别。此外,在创建图像时设置 RenderingHints 也没有什么区别。只有当它们使用AffineTransform
旋转并使用g.drawImage(...)
绘制时,它们似乎才会模糊。 上图显示了默认(最近邻)和双线性插值之间的区别。
这是我目前绘制它们的方式(比 TexturePaint 快得多):
// GamePanel.java
private void paintCard(Graphics2D g, int code, int x, int y)
g.drawImage(imageLoader.getCard(code), x, y, 86, 126, null);
// ImageLoader.java
public BufferedImage getCard(int code)
return cardImages[code];
我所有的卡片都是80x120,阴影.png是86x126,这样在卡片周围留下3px的半透明阴影。我知道这不是一个真实的阴影,但看起来还不错。
所以问题就变成了...... 如何在旋转 BufferedImage 时产生清晰的绘制结果?
参考上一个关于扇形牌手的问题:How can you detect a mouse-click event on an Image object in Java?
赏金编辑: 好的,经过多次讨论,我制作了一些测试 .svg 卡,看看 SVG Salamander 将如何渲染它们。不幸的是,性能很糟糕。我的实现足够干净,看到双缓冲 BufferedImage 的绘画速度非常快。这意味着我已经完成了一个完整的循环,我又回到了原来的问题。
我会将 50 赏金奖励给任何能给我提供解决方案来获得锐利 BufferedImage 旋转的人。建议在绘画之前使图像大于所需大小并缩小比例,并使用双三次插值。如果这些是唯一可能的解决方案,那么我真的不知道从哪里开始,我可能只需要处理模糊的旋转 - 因为这两者都会造成性能挫折。
如果我能找到办法做好这件事,我就能完成我的游戏。 谢谢大家。 :)
【问题讨论】:
我看不到你在哪里设置RenderingHints
。
@Moonbeam:RenderingHints 对图像没有帮助,而仅对我们绘制的形状有帮助。如果您查看 API,它指出:“ANTIALIASING 提示控制 Graphics2D 对象的几何渲染方法是否会尝试减少沿形状边缘的锯齿伪影。”所以他是对的甚至不要在这里尝试使用它们。
@Rudi:如果你在内存图像中保持更大的尺寸,并在可视化之前旋转后缩小尺寸怎么办?
@paranoid-android 将图像旋转任意角度意味着破坏数据;图像越大,丢失的数据越少,因此如果您负担不起切换到矢量图形,它可能是唯一的选择
@paranoid-android 因为我只给出了一个提示而没有实现,如果你用一些示例代码回答自己并接受它会更好。请注意,我没有建议放大(这是有损的),只是为了在可视化之前保持更大的图像并缩小
【参考方案1】:
旋转光栅化图像(例如 BufferedImage)时,会丢失数据。最好的解决方案是将图像保存得比您需要的大,并在绘制时动态缩小。我发现你需要的 1.5 倍大小是一个很好的起点。
然后,当您绘制图像时,动态调整大小:
g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);
推荐使用双线性插值进行旋转。
建议归功于 guido。
【讨论】:
【参考方案2】:这个建议在你的设计中可能有点晚了,但可能值得一提。
如果大量旋转和动画是 UI 的一部分,那么光栅化图像可能是不适合使用的技术;特别是对于具有大量曲线的复杂图像。等到你尝试缩放你的画布。我可能会建议查看基于矢量的图形库。它们将呈现您想要的各种效果,而产生伪影的可能性较小。
http://xmlgraphics.apache.org/batik/using/swing.htm
【讨论】:
感谢您指出这一点。下次我会考虑这种方法。事实上,我的游戏是可扩展的,所有的宽度、高度和位置都与面板大小相关。我已经检查了它们,并且正在以正确的尺寸绘制卡片。在上面的示例中,我只是对尺寸进行了硬编码,以适应默认面板尺寸 1280x720。 @paranoid:我想我在某处看到过公共领域的 SVG 图像打牌,所以已经有合适的图像可供您使用。让我看看能不能再找到它们。 编辑: 这里有一些:svg-cards @Hovercraft Full Of Eels:由于缺乏支持/可靠的答案,我将在路径上重新设计我的卡片组并导出为 SVG 文件,然后使用 Batik 进行转码/渲染。如果这种方法效果很好,我会接受你的建议作为答案。给我几天时间。 @paranoid:这不是我的建议,而是 mbarnes,但无论如何,是的,如果可行,请接受它。祝你好运! @mbarnes 我发现蜡染很难使用。我可能会就它的使用提出一个新问题。【参考方案3】:在AffineTransformOp
中设置插值类型以及抗锯齿值可能会带来一些改进。键入TYPE_BICUBIC
,虽然速度较慢,但通常质量最好;概述了一个示例here。请注意,您可以提供多个RenderingHints
。另一个陷阱来自于每次渲染图像时未能应用提示。您可能还需要调整背景的透明度,如here 建议的那样。最后,考虑创建一个sscce,其中包含您的一张实际图像。
【讨论】:
我今天将创建一个 sscce,因为 AffineTransformOp 似乎没有做任何不同的事情 - 假设我使用它正确。我在哪里以及如何发布/分享 sscce?谢谢。 您可以编辑您的问题,粘贴代码,选择它,单击
图标,然后保存编辑后的问题。还有一个图像图标。由于RenderingHints
的行为本质上是暗示性的,因此您的主机操作系统和Java 平台版本可能是相关的。
啊希望我所做的不会太远?见编辑。谢谢。
我发布了截图here。我看到抗锯齿或双三次插值有明显的好处,尽管平台可能会有所不同。
谢谢。我可以看到双三次也有轻微的改进,但是渲染太慢了,真的不值得。我可能会考虑在绘制之前将图像文件放大并缩小尺寸。以上是关于旋转 BufferedImage 时如何产生清晰的绘制结果?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不产生 y 旋转抖动的情况下统一使用触摸输入来钳制游戏对象的 z 旋转