开发者

How can you produce sharp paint results when rotating a BufferedImage?

开发者 https://www.devze.com 2023-03-21 01:08 出处:网络
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 is

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:

How can you produce sharp paint results when rotating a BufferedImage?

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:

How can you produce sharp paint results when rotating a BufferedImage?

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 com开发者_如何学Pythone 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. :)


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.


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号