将旋转的图像堆叠成一个组合图像

时间:2017-04-28 22:11:18

标签: java image graphics bufferedimage affinetransform

我正在写一个扑克游戏,我正在尝试写一个产生特定手牌图像的课程。我最初只是通过将5张卡片中的每张卡片的图像组合在一起来制作图像。这就是结果:

enter image description here

然后我决定,将牌叠放在一起并展开的手牌会更好,就像拿一张牌一样。

这是迄今为止我能做到的最好的事情:

enter image description here

正如你所看到的,最后3张牌看起来应该是这样,但前三张牌被切断到第三张牌的左边。

这是我现在的代码(它不是最干净的,因为我一直试图让它工作,无论它需要什么)

private static final int CARD_WIDTH = 500;
private static final int CARD_HEIGHT = 726;
private static final double ROTATION = 20.0;

public void createImage(HandOfCards hand) throws IOException {
    int handImageWidth = (int) (2 * (Math.sin(degreesToRadian(ROTATION)) * CARD_HEIGHT + Math.cos(degreesToRadian(ROTATION)) * CARD_WIDTH)- CARD_WIDTH);
    int handImageHeight = (int) (CARD_HEIGHT + Math.sin(degreesToRadian(ROTATION)) * CARD_WIDTH); 

    BufferedImage handImage = new BufferedImage(handImageWidth, handImageHeight, BufferedImage.TYPE_INT_ARGB);
    Graphics2D graphics = (Graphics2D) handImage.getGraphics();

    int xPos = handImageWidth / 2 - CARD_WIDTH / 2;
    int yPos = 0;
    int xAnchor = CARD_WIDTH; 
    int yAnchor = CARD_HEIGHT;

    double rotation = -ROTATION;        
    for (int i = 0; i < HandOfCards.HAND_SIZE; i++) {
        if (i == 3) xAnchor = 0;

        PlayingCard card = hand.getCard(i);
        BufferedImage cardImage = ImageIO.read(new File("cardImages/" + card + ".png"));

        AffineTransform transform = new AffineTransform();
        transform.rotate(degreesToRadian(rotation), xAnchor, yAnchor);
        AffineTransformOp transformOp = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
        cardImage = transformOp.filter(cardImage, null);

        graphics.drawImage(cardImage, xPos, yPos, null);
        rotation += ROTATION / 2;
    }

private double degreesToRadian(double degrees) {
    return (degrees * Math.PI) / 180.0;
}

编辑

为了让事情变得更清楚,以下是仅在首次执行循环时(仅绘制第一张卡片)并且背景颜色为显示整个图像的大小的结果。

enter image description here

1 个答案:

答案 0 :(得分:1)

您正在观察的行为的原因在documentation of AffineTransformOp#filter

中说明
  

getBounds2D(BufferedImage)返回的矩形坐标不一定与此方法返回的BufferedImage的坐标相同。 如果矩形的左上角坐标为负,则不绘制矩形的这部分

当您使用类似

的声明打印每张卡片的边界时
System.out.println("Bounds: "+transformOp.getBounds2D(cardImage));

你会看到边界是负面的(正如人们在将卡片向左旋转时所期望的那样)。

可以通过将AffineTransform调整为始终生成边界,并使用非filter目标图片调用null方法来避免这种情况(在你的情况下:使用包含手的图像 - 即所有卡片图像。)

(这是问题的实际答案。其余部分可能会被忽略,或被视为我有太多空闲时间的证据)

话虽如此,我想建议一个不同的解决方案,因为当前方法在不同层面存在一些问题。

在最高级别:为什么您要创建此图片根本?我想你正在实施一个纸牌游戏。在这样的游戏中,你可能有数百种不同的手#34;为什么要为每个手创建图像?

您可以直接绘制旋转的图像,而不是为每只手创建图像。粗略地说:除了将图像绘制到新图像的Graphics之外,您可以将它们简单地绘制到Graphics的{​​{1}}中,在那里你可以用手画画。

但是考虑到差异只是你正在绘制的JPanel对象,这可以在以后很容易地改变(如果相应地实现),也许有一个实际的原因让你创建这些图像。

在最低级别:函数Graphics应完全替换为Math.toRadians

以下是一个示例,实现为MCVE。 (这意味着它不使用degreesToRadianHandOfCards类。而是在PlayingCards个对象列表上运行。这些图像实际上是在运行时从维基百科下载的。< / p>

此示例的核心是BufferedImage。它允许您将(旋转的)卡片绘制成RotatedPlayingCardsPainter。尝试时,这种方法的一个优点可能会变得很明显:您可以使用滑块动态更改卡之间的角度。 (想象一下你的游戏在某种奇特的动画......)

(但如果您愿意,它还包含一个Graphics2D方法,可以让您创建一个图像,就像最初在问题中所做的那样)

当您阅读代码时,您会看到每个卡的createImage实例是在AffineTransform方法中创建的。在那里,我添加了一些任意的,神奇的因素来轻松地将卡片相互移动,并给他们一个更像粉丝般的&#34;出现。

比较此图片,没有神奇因素

Rotated cards no magic

一个神奇的因素:

Rotated playing cards

我认为后者看起来更像现实&#34;但这可能是品味问题。

另一方面注意:直接绘制图像(与createTransform方法相比)的缺点是图像的边框可能看起来是锯齿状的,无论过滤和抗锯齿设置如何。这是因为在图像的边界处没有任何内插。在给定的程序中,这是通过AffineTransformOp方法规避的,它为图像添加了1像素的透明边框,以确保它看起来很漂亮,并且在旋转图像时边框看起来很平滑。

以下是代码:

addBorder