如何在Java中旋转一个非正方形的图像?

5
我最近看到了一个与Java中如何旋转图像有关的问题(此处为外部链接),我直接从那个答案中复制/粘贴了代码。实践证明,它似乎只能旋转正方形图像(即具有相同大小、宽度和高度的图像)。当我尝试将其用于非正方形图像时,它似乎会切掉那部分使其成为矩形的部分,如果这样说的话。就像这样我该如何解决/解决这个问题? 编辑:我正在使用的代码。此外,由于这将是一个“游戏”,因此我不会有滚动条,并且也不会一直处于全屏状态。
public class Player extends Entity { //Entity has basic values such as (float) x & y values, along with some getters and setters
   double theta;    

   Reticle reticle; //draws a reticle where the cursor was(basically just replaces java.awt.Cursor due to something not neccessary for me to get into)
   Sprite currentImage; //basically just a BufferedImage that you can apply aspect ratios to

   //constructor

   @Override
   public void tick() {
      //(this line) gets the Reticle from the main-method class and set it to this reticle object
      reticleX = reticle.getX(); //basically gets the mouse coordinates
      reticleY = reticle.getY();

      x += dX; //delta or change in X
      y += dY  //delta or change in Y

      checkCollision(); //bounds checking

      //getCentralizedX()/Y() gets the center of the currentImage
      theta = getAngle(getCentralizedX(), getCentralizedY(), reticleX, reticleY);

      currentImage = Images.rotateSprite(currentImage, theta);
    }

    @Override
    public void render(Graphics g) {
        currentImage.render(g, x, y);
        g.drawLine((int) getCentralizedX(), (int) getCentralizedY(), (int) reticleX, (int) reticleY);
    }

    public double getAngle(float startX, float startY, float goalX, float goalY) {
        double angle = Math.atan2(goalY - startY, goalX - startX);
        //if(angle < 0) { //removed this as this was the thing messing up the rotation
            //angle += 360;
        //}
    }

如果士兵的角度在90 < angle < 270之间,那么它是(基本上)正确的,但如果它的角度在90 > angle > 270,则会有一些问题。这里有一些图片。问题不在于瞄准线(蓝色线)的角度不正确。

删除了所有图片,因为在getAngle()中移除了if(angle < 0)可以解决旋转错误的问题。现在唯一的问题是它不能原地旋转。

编辑2:我用相同的方法制作的SSCCE,在某些情况下会出现问题。

public class RotateEx extends Canvas implements Runnable {
Player player;

public RotateEx(BufferedImage image) {
    player = new Player(image, 50, 50);
    setPreferredSize(new Dimension(600, 600));
}

public void setDegrees(int degrees) {
    player.theta = Math.toRadians(degrees);
}

public BufferedImage rotateImage(BufferedImage original, double theta) {
    double cos = Math.abs(Math.cos(theta));
    double sin = Math.abs(Math.sin(theta));
    double width = original.getWidth();
    double height = original.getHeight();
    int w = (int) (width * cos + height * sin);
    int h = (int) (width * sin + height * cos);

    BufferedImage out = new BufferedImage(w, h, original.getType());
    Graphics2D g2 = out.createGraphics();
    double x = w / 2; //the middle of the two new values 
    double y = h / 2;

    AffineTransform at = AffineTransform.getRotateInstance(theta, x, y);
    x = (w - width) / 2;
    y = (h - height) / 2;
    at.translate(x, y);
    g2.drawRenderedImage(original, at);
    g2.dispose();

    return out;
}

public void tick() {
    player.tick();
}

public void render() {
    BufferStrategy bs = this.getBufferStrategy();
    if(bs == null) {
        createBufferStrategy(4);
        return;
    }

    Graphics g = bs.getDrawGraphics();
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, getWidth(), getHeight());
    player.render(g);

    g.dispose();
    bs.show();
}

public static void main(String args[]) throws IOException, InterruptedException {
    String loc = "FILELOCATION"; //of course this would be a valid image file
    BufferedImage image = ImageIO.read(new File(loc));

    final RotateEx ex = new RotateEx(image);
    final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
    slider.addChangeListener(new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            int value = slider.getValue();
            ex.setDegrees(value);
        }
    });

    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.add(ex);
    f.add(slider, BorderLayout.SOUTH);
    f.pack();
    f.setLocationRelativeTo(null);
    f.setVisible(true);

    new Thread(ex).start();
}

@Override
public void run() {
    long lastTime = System.nanoTime();
    final double numTicks = 60.0;
    double n = 1000000000 / numTicks;
    double delta = 0;
    int frames = 0;
    int ticks = 0;
    long timer = System.currentTimeMillis();

    while (true) {
        long currentTime = System.nanoTime();
        delta += (currentTime - lastTime) / n;
        lastTime = currentTime;

        render();
        tick();
        frames++;

        if (delta >= 1) {
            ticks++;
            delta--;
        }
    }
}

class Player {
    public float x, y;
    int width, height;
    public double theta; //how much to rotate, in radians
    BufferedImage currentImage; //this image would change, according to the animation and what frame its on

    public Player(BufferedImage image, float x, float y) {
        this.x = x;
        this.y = y;
        width = image.getWidth();
        height = image.getHeight();
        currentImage = image;
    }

    public void tick() {
        currentImage = rotateImage(currentImage, theta);
    }

    public void render(Graphics g) {
        g.drawImage(currentImage, (int) x, (int) y, null);
    }
}

}

2个回答

5
当你旋转一张图片时,宽度和高度也会改变,而你的代码没有考虑到这一点。
这是我找到的一段旧代码,应该能更好地解决这个问题:
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;

public class Rotation
{
    BufferedImage image;
    JLabel label;

    public Rotation(BufferedImage image)
    {
        this.image = image;
    }

    private BufferedImage getImage(double theta)
    {
        //  Determine the size of the rotated image

        double cos = Math.abs(Math.cos(theta));
        double sin = Math.abs(Math.sin(theta));
        double width  = image.getWidth();
        double height = image.getHeight();
        int w = (int)(width * cos + height * sin);
        int h = (int)(width * sin + height * cos);

        //  Rotate and paint the original image onto a BufferedImage

        BufferedImage out = new BufferedImage(w, h, image.getType());
        Graphics2D g2 = out.createGraphics();
        g2.setPaint(UIManager.getColor("Panel.background"));
        g2.fillRect(0,0,w,h);
        double x = w/2;
        double y = h/2;
        AffineTransform at = AffineTransform.getRotateInstance(theta, x, y);
        x = (w - width)/2;
        y = (h - height)/2;
        at.translate(x, y);
        g2.drawRenderedImage(image, at);
        g2.dispose();
        return out;
    }

    private JLabel getLabel()
    {
        ImageIcon icon = new ImageIcon(image);
        label = new JLabel(icon);
        label.setHorizontalAlignment(JLabel.CENTER);
        return label;
    }

    private JSlider getSlider()
    {
        final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
        slider.addChangeListener(new ChangeListener()
        {
            public void stateChanged(ChangeEvent e)
            {
                int value = slider.getValue();
                BufferedImage bi = getImage(Math.toRadians(value));
                label.setIcon(new ImageIcon(bi));
            }
        });
        return slider;
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                try
                {
                    String path = "mong.jpg";
                    ClassLoader cl = Rotation.class.getClassLoader();
                    BufferedImage bi = ImageIO.read(cl.getResourceAsStream(path));
                    Rotation r = new Rotation(bi);
                    JFrame f = new JFrame();
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    f.getContentPane().add(new JScrollPane(r.getLabel()));
                    f.getContentPane().add(r.getSlider(), "South");
                    f.pack();
                    f.setLocation(200,200);
                    f.setVisible(true);
                }
                catch(IOException e)
                {
                    System.out.println(e);
                }
            }
        });
    }
}

编辑:

另一个选项是创建一个图标,然后可以使用旋转图标。然后,在绘制代码中,您可以旋转和绘制该图标。例如:

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;

public class Rotation3 extends JPanel
{
    private Icon icon;
    private Icon rotated;
    private int degrees;

    public Rotation3(BufferedImage image)
    {
        icon = new ImageIcon( image );
        setDegrees( 0 );
        setPreferredSize( new Dimension(600, 600) );
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        double radians = Math.toRadians( degrees );
        rotated = new RotatedIcon(icon, degrees);

        // translate x/y so Icon is rotated around a specific point (300, 300)

        int x = 300 - (rotated.getIconWidth() / 2);
        int y = 300 - (rotated.getIconHeight() / 2);
        rotated.paintIcon(this, g, x, y);

        g.setColor(Color.RED);
        g.fillOval(295, 295, 10, 10);
    }

    public void setDegrees(int degrees)
    {
        this.degrees = degrees;
        double radians = Math.toRadians( degrees );
        rotated = new RotatedIcon(icon, degrees);
        repaint();
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                try
                {
                    String path = "dukewavered.gif";
                    ClassLoader cl = Rotation3.class.getClassLoader();
                    BufferedImage bi = ImageIO.read(cl.getResourceAsStream(path));
                    final Rotation3 r = new Rotation3(bi);

                    final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
                    slider.addChangeListener(new ChangeListener()
                    {
                        public void stateChanged(ChangeEvent e)
                        {
                            int value = slider.getValue();
                            r.setDegrees( value );
                        }
                    });

                    JFrame f = new JFrame();
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    f.add(new JScrollPane(r));
                    f.add(slider, BorderLayout.SOUTH);
                    f.pack();
                    f.setLocationRelativeTo(null);
                    f.setVisible(true);
                }
                catch(IOException e)
                {
                    System.out.println(e);
                }
            }
        });
    }
}

编辑2:
比我想象的更简单,这里有一个例子:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;

public class Rotation2 extends JPanel
{
    BufferedImage image;
    int degrees;
    int point = 250;

    public Rotation2(BufferedImage image)
    {
        this.image = image;
        setDegrees( 0 );
        setPreferredSize( new Dimension(600, 600) );
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D)g.create();

        double radians = Math.toRadians( degrees );
        g2.translate(point, point);
        g2.rotate(radians);
        g2.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
        g2.drawImage(image, 0, 0, null);

        g2.dispose();

        g.setColor(Color.RED);
        g.fillOval(point - 5, point - 5, 10, 10);
    }

    public void setDegrees(int degrees)
    {
        this.degrees = degrees;
        repaint();
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                try
                {
//                  String path = "mong.jpg";
                    String path = "dukewavered.gif";
                    ClassLoader cl = Rotation2.class.getClassLoader();
                    BufferedImage bi = ImageIO.read(cl.getResourceAsStream(path));
                    final Rotation2 r = new Rotation2(bi);

                    final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
                    slider.addChangeListener(new ChangeListener()
                    {
                        public void stateChanged(ChangeEvent e)
                        {
                            int value = slider.getValue();
                            r.setDegrees( value );
                        }
                    });

                    JFrame f = new JFrame();
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    f.add(new JScrollPane(r));
                    f.add(slider, BorderLayout.SOUTH);
                    f.pack();
                    f.setLocationRelativeTo(null);
                    f.setVisible(true);
                }
                catch(IOException e)
                {
                    System.out.println(e);
                }
            }
        });
    }
}

旋转代码来自:如何在Swing中逐渐旋转图像?


你说得对,我误解了。我现在正在编写一个。在您最近的编辑中,它不适用于我是因为我认为它旋转了整个图形,而我希望旋转方法提供一个“BufferedImage”和一个弧度值,并将其旋转那么多弧度,在“BufferedImage”的中心返回一个新的旋转“BufferedImage”。您能否进一步描述一下您所说的“如果您有一个普通的图像(100 x 50),中心点是(50,50),而不是(50,25)。因此,您在(0,25)处绘制图像。”的含义? - Gannon Prudhomme
我的IDE(Eclipse Luna)似乎认为RotatedIcon不是一个有效的类。我在谷歌上搜索了一下,甚至找不到Oracle关于它的文档。它不是一个单独的库,对吧? - Gannon Prudhomme
哦,我之前点击过它,但是一旦网站知道你已经点击过了,它似乎就会混合在一起,所以我几乎看不出区别,因为我专注于代码。另外,我能不能只使用宽度和高度来确定中心位置,或者不行,因为那些宽度/高度计算(使用余弦和正弦来计算“新”值)。如果我改变它来接受中心x/y,那么它将真正破坏我的边界检查。 - Gannon Prudhomme
我之前点击过它,但是一旦网站知道你已经点击过,它似乎就会混合在一起。是的,我有同样的抱怨。他们一个月前“升级”了论坛的LAF并更改了已查看链接的颜色。我也几乎看不出区别。 “这将真正破坏我的所有边界检查。” - 好吧,这是一个不同的问题,因为图像的大小随着图像旋转而改变。我想现在您需要跟踪绘制图像的x / y位置。无论如何,关于围绕其中心旋转图像的问题已经得到解决(以多种方式)。 - camickr
说实话,我有些无礼和不够感激。我曾经认为这个方法不管用,而且尽可能地避免使用库,所以有点忽视了 RotatedIcon 。但是今天早上我试图实现它,结果它完美地运作了。我无法感谢你们的帮助。 - Gannon Prudhomme
显示剩余20条评论

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接