在 Java 中旋转缓冲图像

Posted

技术标签:

【中文标题】在 Java 中旋转缓冲图像【英文标题】:Rotate a buffered image in Java 【发布时间】:2016-10-12 00:01:59 【问题描述】:

我正在尝试在 java 中旋转缓冲图像。这是我正在使用的代码:

public static BufferedImage rotate(BufferedImage bimg, double angle) 
    int w = bimg.getWidth();
    int h = bimg.getHeight();
    Graphics2D graphic = bimg.createGraphics();
    graphic.rotate(Math.toRadians(angle), w / 2, h / 2);
    graphic.drawImage(bimg, null, 0, 0);
    graphic.dispose();
    return bimg;

我查看了有关此主题的大量堆栈溢出问题和答案,但无法弄清楚为什么图像会像我尝试旋转它时那样被切碎。这是一个显示加载图像的示例: loaded image

单击旋转按钮后,该按钮使用缓冲图像调用上述函数,角度为 90.0: chopped up image

有人可以帮助我了解正在发生的事情以及如何解决它吗?

【问题讨论】:

【参考方案1】:

与往常一样,互联网来救援。所以,这是我从其他资源/帖子/博客中蹒跚而行的一些代码,它将返回一个新图像,该图像的大小将包含旋转后的图像

public BufferedImage rotateImageByDegrees(BufferedImage img, double angle) 
    double rads = Math.toRadians(angle);
    double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
    int w = img.getWidth();
    int h = img.getHeight();
    int newWidth = (int) Math.floor(w * cos + h * sin);
    int newHeight = (int) Math.floor(h * cos + w * sin);

    BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = rotated.createGraphics();
    AffineTransform at = new AffineTransform();
    at.translate((newWidth - w) / 2, (newHeight - h) / 2);

    int x = w / 2;
    int y = h / 2;

    at.rotate(rads, x, y);
    g2d.setTransform(at);
    g2d.drawImage(img, 0, 0, this);
    g2d.setColor(Color.RED);
    g2d.drawRect(0, 0, newWidth - 1, newHeight - 1);
    g2d.dispose();

    return rotated;

更新

所以,使用这个 PNG:

还有这段代码……

package javaapplication1.pkg040;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test 

    public static void main(String[] args) 
        new Test();
    

    public Test() 
        EventQueue.invokeLater(new Runnable() 
            @Override
            public void run() 
                try 
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                 catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) 
                    ex.printStackTrace();
                

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            
        );
    

    public class TestPane extends JPanel 

        private BufferedImage master;
        private BufferedImage rotated;

        public TestPane() 
            try 
                master = ImageIO.read(new File("/Volumes/Disk02/Dropbox/MegaTokyo/Miho_Small.png"));
                rotated = rotateImageByDegrees(master, 0.0);
             catch (IOException ex) 
                ex.printStackTrace();
            

            Timer timer = new Timer(40, new ActionListener() 
                private double angle = 0;
                private double delta = 1.0;

                @Override
                public void actionPerformed(ActionEvent e) 
                    angle += delta;
                    rotated = rotateImageByDegrees(master, angle);
                    repaint();
                
            );
            timer.start();
        

        @Override
        public Dimension getPreferredSize() 
            return master == null
                         ? new Dimension(200, 200)
                         : new Dimension(master.getWidth(), master.getHeight());
        

        @Override
        protected void paintComponent(Graphics g) 
            super.paintComponent(g);
            if (rotated != null) 
                Graphics2D g2d = (Graphics2D) g.create();
                int x = (getWidth() - rotated.getWidth()) / 2;
                int y = (getHeight() - rotated.getHeight()) / 2;
                g2d.drawImage(rotated, x, y, this);
                g2d.dispose();
            
        

        public BufferedImage rotateImageByDegrees(BufferedImage img, double angle) 

            double rads = Math.toRadians(angle);
            double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
            int w = img.getWidth();
            int h = img.getHeight();
            int newWidth = (int) Math.floor(w * cos + h * sin);
            int newHeight = (int) Math.floor(h * cos + w * sin);

            BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = rotated.createGraphics();
            AffineTransform at = new AffineTransform();
            at.translate((newWidth - w) / 2, (newHeight - h) / 2);

            int x = w / 2;
            int y = h / 2;

            at.rotate(rads, x, y);
            g2d.setTransform(at);
            g2d.drawImage(img, 0, 0, this);
            g2d.dispose();

            return rotated;
        
    


我可以生成类似...

【讨论】:

奇怪的是,我正在使用的图像不会旋转并锁定我的程序。这些图像是通过将二进制文件中的值转换为灰度来创建的,然后存储在缓冲图像中。我尝试了一个保存到 PNG 的文件,但它也无法旋转。唯一会旋转的图像是从 JPEG 格式加载的图像。有什么想法吗? 所以,我使用基于 PNG 的图像重新测试了代码,它对我来说效果很好。我建议您考虑提出一个新问题并提供一个可运行的示例来演示您的问题,因为从我的角度来看,它似乎与您使用代码的“方式”更相关 @MadProgrammer Fair 无处不在。我也想知道为什么,但我的想法是,如果你知道是谁,你就能找出原因。啊,但是.... c`est la vie. @GitPhilter 你看到g2d.setTransform(at);这行了吗?这是对 Graphics 上下文应用一个转换,它定义了旋转逻辑 @GitPhilter 好的,所以rotateImageByDegrees 正在获取原始图像并将其旋转一定度数。这会生成一个新的BufferedImagepaintComponent 只是将旋转后的图像绘制到屏幕上,因此您可以看到结果。因此,您可以使用 rotateImageByDegrees 并旋转图像,我不知道,保存它或做任何您想做的事情 - 或者,如果您愿意,就像示例一样,为它制作动画【参考方案2】:

您会得到混乱的图像结果,因为您将旋转后的图像绘制到输入图像本身。相反,您需要从新的 BufferedImage 创建图形。

public static BufferedImage rotate(BufferedImage bimg, double angle) 

    int w = bimg.getWidth();    
    int h = bimg.getHeight();

    BufferedImage rotated = new BufferedImage(w, h, bimg.getType());  
    Graphics2D graphic = rotated.createGraphics();
    graphic.rotate(Math.toRadians(angle), w/2, h/2);
    graphic.drawImage(bimg, null, 0, 0);
    graphic.dispose();
    return rotated;

请注意,如果您想避免出现裁剪角,则需要调整输出 BufferedImage 的宽度和高度。

【讨论】:

【参考方案3】:

此代码从文件中读取图像,将其旋转一定角度,然后写入另一个文件。它适用于具有透明度的 png 图像:

public static void main(String[] args) throws IOException 
    BufferedImage image = ImageIO.read(
            Test.class.getResourceAsStream("/resources/image.png"));

    BufferedImage rotated = rotateImage(image, 45);

    ImageIO.write(rotated, "png",
            new FileOutputStream("resources/rotated.png"));

private static BufferedImage rotateImage(BufferedImage buffImage, double angle) 
    double radian = Math.toRadians(angle);
    double sin = Math.abs(Math.sin(radian));
    double cos = Math.abs(Math.cos(radian));

    int width = buffImage.getWidth();
    int height = buffImage.getHeight();

    int nWidth = (int) Math.floor((double) width * cos + (double) height * sin);
    int nHeight = (int) Math.floor((double) height * cos + (double) width * sin);

    BufferedImage rotatedImage = new BufferedImage(
            nWidth, nHeight, BufferedImage.TYPE_INT_ARGB);

    Graphics2D graphics = rotatedImage.createGraphics();

    graphics.setRenderingHint(
            RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BICUBIC);

    graphics.translate((nWidth - width) / 2, (nHeight - height) / 2);
    // rotation around the center point
    graphics.rotate(radian, (double) (width / 2), (double) (height / 2));
    graphics.drawImage(buffImage, 0, 0, null);
    graphics.dispose();

    return rotatedImage;

image.png rotated.png (45º) ⟳ rotated.png (-45º) ⟲

【讨论】:

【参考方案4】:

您必须考虑调整大小以及输出的新宽度和高度。见:https://***.com/a/4787898/5420880

【讨论】:

我不确定 GraphicsConfiguration gc = getDefaultConfiguration();作品。我用谷歌搜索,发现你需要 GraphicsEnvironment,然后是 GraphicsDevice。不知道如何使用它。我复制了推荐链接中的函数,它会导致错误。

以上是关于在 Java 中旋转缓冲图像的主要内容,如果未能解决你的问题,请参考以下文章

打印在 Java 中旋转的图像会增加伪影

Java:旋转图像

在java中将图像旋转90度[重复]

为啥加入小缓冲图像的 Java 代码不起作用?

java应用程序中的缓冲图像问题

Java2D:如何将图像放置在具有正确旋转的中心周围