1

In my application, there are things marked on an image by clicking on the image.
That is done by setting the image as an Icon to a JLabel and adding a MousePressed method
I want to add a feature for the users to redo the last step now and need a backupimage for that.

The following is a code example:

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()));
    }
}

It is not working as intended.

If I use the commented line backupscaledimg = getScaledImage(buffimg,label.getWidth(),label.getHeight()); instead of backupscaledimg = scaledimg;, it is working as expected.

The problem is that I want to do several steps of drawing things on the image, and it can only be redone the first time like that. From what I know the problem might be, that the command backupscaledimg = scaledimg is only creating a pointer for backupscaledimg pointing to scaledimg which results in both of them altering.

I don't want to save a new imagefile on each step though.
Is there any way around this? (found the scaling function on here by the way thanks for that anonymous)

almightyGOSU
  • 3,731
  • 6
  • 31
  • 41
  • Ok I just tested this again and the "working" line that I had blocked in the above example works just once which confuses me even more. After redoing them with a klick on the button and drawing some new things it doesnt work anymore. Not that I wanted to use that anyway but strange though. – ambitious java apprentice Jul 24 '15 at 08:13
  • `=` indeed only assigns the reference, so you need to copy the image when you want to back it up. As for the reset working only once, you need to copy there too, for that same reason - otherwise you're drawing on your "backup". As a side note, stay away from `null` layouts for lots of reasons. – kiheru Jul 24 '15 at 08:37
  • right so i wasnt completely on the wrong horse. allow the newb the question: how is copying the image done? Thanks for your side note as well, i recently learned that and am using grid layout. I have just put a fast and dirty code snippet for you. – ambitious java apprentice Jul 24 '15 at 08:41
  • @ambitiousjavaapprentice so what are you trying to do? I am trying out your code now, there is only a restart button, and "test" gets added to the image when you click on it. – almightyGOSU Jul 24 '15 at 08:43
  • The simplest is creating an empty image of the same dimensions, and drawing the original image on it. – kiheru Jul 24 '15 at 08:48
  • i want to remove everything that has been drawn onto the image. and since there are several steps (three steps to be specific) in which things are drawn on the image i want the users to abled to reset the recent step. just in case they klicked in the wrong place and things are drawn in the wrong place. if one misclicks on the third step for example one doesnt have to restart the application but can reset the third step and start over (with having drawn things two times correctly already). – ambitious java apprentice Jul 24 '15 at 08:49
  • @ambitiousjavaapprentice Are you able to backtrack all the way to the original image? – almightyGOSU Jul 24 '15 at 08:53

1 Answers1

0

Made some changes

  • Using GridBagLayout
  • Able to reset back to original image by pressing "Restart Step"
  • Able to undo last action with the use of an image stack (i.e. a Stack of BufferedImage)

Example code:

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()));
    }
}

Edited Image:

Edited

Image after pressing "Restart step":

Cleared

Pressing "Undo Last" when image stack is empty:

EmptyImageStack

Note:

Community
  • 1
  • 1
almightyGOSU
  • 3,731
  • 6
  • 31
  • 41
  • if only i had known that all i had to do was copying the image properly ;) thanks for that productive answer. nice testing image as well. thanks for making the unnessecary work of adding a proper layout. i have one in my program but since i just wanted to make a quick and dirty example of what is happening i didnt put one. – ambitious java apprentice Jul 24 '15 at 09:02
  • @ambitiousjavaapprentice No problem! :) – almightyGOSU Jul 24 '15 at 09:12