在 Java 中绘制的图像的备份

Backup for image that is drawn on in Java

在我的应用程序中,通过单击图像在图像上标记了一些东西。
这是通过将图像设置为 JLabel 的图标并添加 MousePressed 方法来完成的
我想为用户添加一个功能来重做最后一步,并且需要一个备份图像。

以下是代码示例:

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.awt.Color;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class BuffImgTest {

    private BufferedImage buffimg, scaledimg, backupscaledimg;
    private Image img;
    private JFrame frame;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    BuffImgTest window = new BuffImgTest();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public BuffImgTest() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(null);

        final JLabel label = new JLabel("");
        label.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent arg0) {
                Graphics graphics = scaledimg.getGraphics();
                graphics.setColor(Color.RED);
                graphics.drawString("Test", arg0.getX(), arg0.getY());
                Image img = Toolkit.getDefaultToolkit().createImage(
                        scaledimg.getSource());
                label.setIcon(new ImageIcon(img));
            }
        });

        label.setBounds(10, 34, 414, 201);

        try {
            buffimg = ImageIO.read(new File("test.jpg"));
            scaledimg = getScaledImage(buffimg, label.getWidth(),
                    label.getHeight());

            backupscaledimg = scaledimg;
            // backupscaledimg=getScaledImage(buffimg,label.getWidth(),label.getHeight());
            Image img = Toolkit.getDefaultToolkit().createImage(
                    scaledimg.getSource());
            label.setIcon(new ImageIcon(img));

        } catch (Exception e) {
            System.out.println(e);
        }
        frame.getContentPane().add(label);

        JButton btnNewButton = new JButton("Restart Step");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                scaledimg = backupscaledimg;
                Image img = Toolkit.getDefaultToolkit().createImage(
                        scaledimg.getSource());
                label.setIcon(new ImageIcon(img));
            }
        });
        btnNewButton.setBounds(111, 238, 89, 23);
        frame.getContentPane().add(btnNewButton);
    }

    public static BufferedImage getScaledImage(BufferedImage image, int width,
            int height) throws IOException {
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();
        double scaleX = (double) width / imageWidth;
        double scaleY = (double) height / imageHeight;
        AffineTransform scaleTransform = AffineTransform.getScaleInstance(
                scaleX, scaleY);
        AffineTransformOp bilinearScaleOp = new AffineTransformOp(
                scaleTransform, AffineTransformOp.TYPE_BILINEAR);
        return bilinearScaleOp.filter(image, new BufferedImage(width, height,
                image.getType()));
    }
}

它没有按预期工作。

如果我使用注释行 backupscaledimg = getScaledImage(buffimg,label.getWidth(),label.getHeight()); 而不是 backupscaledimg = scaledimg;,它会按预期工作。

问题是我想在图片上做几步画东西,只能像第一次那样重做。据我所知,问题可能在于,命令 backupscaledimg = scaledimg 只是为 backupscaledimg 创建一个指向 scaledimg 的指针,这导致它们都发生了改变。

虽然我不想在每一步都保存一个新的图像文件。
有没有办法解决? (顺便在这里找到了缩放功能感谢匿名)

做了一些修改

  • 使用 GridBagLayout
  • 可以通过按 "Restart Step"
  • 重置回原始图像
  • 能够使用图像堆栈(即 BufferedImage 堆栈)撤消上次操作

示例代码:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Stack;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import java.awt.Color;

import javax.swing.JButton;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class BuffImgTest {

    private BufferedImage scaledImg, originalScaledImg;
    private JFrame frame;

    private Stack<BufferedImage> imageStack = new Stack<>();

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    BuffImgTest window = new BuffImgTest();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public BuffImgTest() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new BorderLayout());

        JPanel panel = new JPanel(new GridBagLayout());

        final JLabel imgLabel = new JLabel("");
        imgLabel.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent arg0) {

                // Add current image to the image stack
                imageStack.add(copyImage(scaledImg));

                // Change the image
                Graphics graphics = scaledImg.getGraphics();
                graphics.setColor(Color.BLACK);
                graphics.setFont(new Font("Arial", Font.BOLD, 32));
                graphics.drawString("TEST", arg0.getX(), arg0.getY());

                // Update label
                Image img = Toolkit.getDefaultToolkit().createImage(
                        scaledImg.getSource());
                imgLabel.setIcon(new ImageIcon(img));
            }
        });

        imgLabel.setSize(500, 500);
        imgLabel.setPreferredSize(new Dimension(500, 500));

        try {
            BufferedImage buffImg = ImageIO.read(new File("test.jpg"));

            scaledImg = getScaledImage(buffImg, imgLabel.getWidth(),
                    imgLabel.getHeight());

            // Clone it first
            originalScaledImg = copyImage(scaledImg);

            Image img = Toolkit.getDefaultToolkit().createImage(
                    scaledImg.getSource());
            imgLabel.setIcon(new ImageIcon(img));

        } catch (Exception e) {
            System.out.println(e);
        }

        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 0;
        c.insets = new Insets(20, 10, 10, 10);
        panel.add(imgLabel, c);

        JButton restartBtn = new JButton("Restart Step");
        restartBtn.setFont(new Font("Arial", Font.ITALIC, 20));
        restartBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {

                // Clear the image stack
                imageStack.clear();

                // Reset the image
                scaledImg = copyImage(originalScaledImg);

                Image img = Toolkit.getDefaultToolkit().createImage(
                        scaledImg.getSource());
                imgLabel.setIcon(new ImageIcon(img));
            }
        });

        c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 1;
        c.anchor = GridBagConstraints.CENTER;
        c.insets = new Insets(10, 10, 5, 10);
        panel.add(restartBtn, c);

        JButton undoBtn = new JButton("Undo Last");
        undoBtn.setFont(new Font("Arial", Font.ITALIC, 20));
        undoBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {

                if(imageStack.isEmpty()) {
                    JOptionPane.showMessageDialog(frame, "Cannot undo anymore!");
                    return;
                }           

                // Get the previous image
                scaledImg = copyImage(imageStack.pop());

                Image img = Toolkit.getDefaultToolkit().createImage(
                        scaledImg.getSource());
                imgLabel.setIcon(new ImageIcon(img));
            }
        });

        c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 2;
        c.anchor = GridBagConstraints.CENTER;
        c.insets = new Insets(5, 10, 20, 10);
        panel.add(undoBtn, c);

        frame.getContentPane().add(panel, BorderLayout.CENTER);
        frame.setSize(800, 800);
    }

    /** For copying image */
    public static BufferedImage copyImage(BufferedImage source){
        BufferedImage b = new BufferedImage(
                source.getWidth(), source.getHeight(), source.getType());
        Graphics g = b.getGraphics();
        g.drawImage(source, 0, 0, null);
        g.dispose();
        return b;
    }

    public static BufferedImage getScaledImage(BufferedImage image, int width,
            int height) throws IOException {
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();
        double scaleX = (double) width / imageWidth;
        double scaleY = (double) height / imageHeight;
        AffineTransform scaleTransform = AffineTransform.getScaleInstance(
                scaleX, scaleY);
        AffineTransformOp bilinearScaleOp = new AffineTransformOp(
                scaleTransform, AffineTransformOp.TYPE_BILINEAR);
        return bilinearScaleOp.filter(image, new BufferedImage(width, height,
                image.getType()));
    }
}

已编辑图像:

按下后的图像"Restart step":

图像堆栈为空时按"Undo Last":

注:

  • 复制图片的代码来自this answer by APerson