旋转 BufferedImage 时如何产生清晰的绘画效果?

发布于 2024-11-24 01:37:09 字数 2950 浏览 2 评论 0 原文

一种尝试的方法是使用 TexturePaintg.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);
}

我应该如何以不同的方式处理这个问题? 谢谢。

编辑:
没有 RenderingHints 的手牌示例
没有 RenderingHints

设置 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 时如何产生清晰的绘制结果?

参考之前的问题,也涉及扇形牌手:
如何检测Java 中图像对象上的鼠标单击事件?

赏金编辑: 好吧,经过多次讨论,我制作了一些测试 .svg 卡,看看 SVG Salamander 如何渲染它们。不幸的是,表现很糟糕。我的实现足够干净,与双缓冲 BufferedImage 一样,绘画速度非常快。这意味着我又回到了原来的问题。

谁能给我一个解决方案来获得锐利的 BufferedImage 旋转,我将悬赏 50 美元。建议在绘制之前使图像比需要的尺寸更大并缩小尺寸,并使用双三次插值。如果这些是唯一可能的解决方案,那么我真的不知道从这里该去哪里,我可能只需要处理模糊的旋转 - 因为这两者都会带来性能挫折。

如果我能找到一种方法来做好这件事,我就能完成我的游戏。 谢谢大家。 :)

One attempted approach was to use TexturePaint and g.fillRect() to paint the image. This however requires you to create a new TexturePaint and Rectangle2D object each time you paint an image, which isn't ideal - and doesn't help anyway.

When I use g.drawImage(BufferedImage,...), the rotated images appear to be blurred/soft.

I'm familiar with RenderingHints and double-buffering (which is what I'm doing, I think), I just find it difficult to believe that you can't easily and efficiently rotate an image in Java that produces sharp results.

Code for using TexturePaint looks something like this.

Grahics2D g2d = (Graphics2D)g; 
g2d.setPaint(new TexturePaint(bufferedImage, new Rectangle2D.Float(0,0,50,50)));
g2d.fillRect(0,0,50,50);

I'm using AffineTransform to rotate a hand of cards into a fan.
What would be the best approach to paint good-looking images quickly?

Here is a screenshot:
Example of blurred rotations

The 9 is crisp but the rest of the cards are definitely not as sharp.

It could be possible that the problem lies in when I create each card image and store it in an array.

Here's how I'm doing it at the moment:

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

How should I approach this differently?
Thanks.

Edit:
Example of hand without RenderingHints

Without RenderingHints

Setting AA hints made no difference. Also, setting RenderingHints when creating the images makes no difference either. It's only when they are being rotated with AffineTransform and painted using g.drawImage(...) that they seem to blur.
The image above shows the difference between default (nearest neighbor) and bilinear interpolation.

Here is how I'm currently painting them (much faster than 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];
  }

All my cards are 80x120 and the shadow .png is 86x126, so as to leave 3px semi-transparent shadow around the card. It's not a realistic shadow I know, but it looks okay.

And so the question becomes... How can you produce sharp paint results when rotating a BufferedImage?

Reference to a previous question also regarding a fanned card hand:
How can you detect a mouse-click event on an Image object in Java?

Bounty-Edit:
Okay so after much discussion I made a few test .svg cards to see how SVG Salamander would go about rendering them. Unfortunately, the performance is terrible. My implementation is clean enough, seeing as with double-buffered BufferedImage's the painting was incredibly fast. Which means I have come full circle and I'm back to my original problem.

I'll give the 50 bounty to whoever can give me a solution to get sharp BufferedImage rotations. Suggestions have been to make the images bigger than they need to be and downscale before painting, and to use bicubic interpolation. If these are the only possible solutions, then I really don't know where to go from here and I may just have to deal with the blurred rotations - because both of those impose performance setbacks.

I can finish my game if I can find a way to do this well.
Thanks to everyone. :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

锦欢 2024-12-01 01:37:09

当您旋转光栅化图像(例如 BufferedImage)时,您会丢失数据。最好的解决方案是将图像保存为大于您需要的图像,并在绘制图像时即时缩小尺寸。我发现 1.5 倍您需要的大小是一个很好的起点。

然后,当您绘制图像时,即时调整大小:

g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);

建议使用双线性插值进行旋转。

建议归功于 guido。

When you rotate a rasterized image (such as a BufferedImage), you lose data. The best solution is to save your images larger than you'll need them, and downscale on the fly when you paint them. I've found that 1.5x the size you need is a good starting point.

Then, when you're painting the image, resize on the fly:

g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);

Rotations using bilinear interpolation is recommended.

Credit for suggestion goes to guido.

平定天下 2024-12-01 01:37:09

这个建议可能在你的设计中有点晚了,但可能值得一提。

如果您的 UI 中有大量旋转和动画,那么光栅化图像可能是错误的技术;特别是对于具有大量曲线的复杂图像。等到你尝试扩大你的画布。我可能建议查看基于矢量的图形库。它们将渲染您想要的各种效果,同时减少伪影的可能性。

http://xmlgraphics.apache.org/batik/using/swing.htm

This advice is probably a little late in your design, but may be worth mentioning.

Rasterized images is probably the wrong technology to use if a lot of rotations and animations are a part of your UI; especially with complicated images with lots of curves. Just wait until you try and scale your canvass. I might suggest looking at a vector based graphical library. They will render the sorts of effects you want with less potential for artifacts.

http://xmlgraphics.apache.org/batik/using/swing.htm

べ繥欢鉨o。 2024-12-01 01:37:09

AffineTransformOp 可能会提供一些改进。类型 TYPE_BICUBIC 虽然速度较慢,但​​通常质量最好; 此处概述了一个示例。请注意,您可以提供多个 RenderingHints。另一个陷阱是由于每次渲染图像时未能应用提示而产生的。您可能还需要调整背景的透明度,如建议的sscce

Setting the interpolation type, as well as anti-aliasing value, in an AffineTransformOp may offer some improvement. Type TYPE_BICUBIC, while slower, is typically the best quality; an example is outlined here. Note that you can supply multiple RenderingHints. Another pitfall arises from failing to apply the hints each time the image is rendered. You may also need to adjust the transparency of the background, as suggested here. Finally, consider creating an sscce that includes one of your actual images.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文