I am trying to create a little drawing pad in one of my programs. This is the class:
class DrawPad extends JComponent {
Image image;
Graphics2D graphics;
int currentX, currentY, oldX, oldY;
public DrawPad() {
setDoubleBuffered(false);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
oldX = e.getX();
oldY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
currentX = e.getX();
currentY = e.getY();
if (graphics != null) {
graphics.drawLine(oldX, oldY, currentX, currentY);
}
repaint();
oldX = currentX;
oldY = currentY;
}
});
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (image == null) {
image = createImage(getSize().width, getSize().height);
graphics = (Graphics2D) image.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
clear();
}
g.drawImage(image, 0, 0, null);
}
public void clear() {
graphics.setPaint(Color.white);
graphics.fillRect(0, 0, getSize().width, getSize().height);
graphics.setPaint(Color.black);
repaint();
}
public void undo() {
// restore previous graphics here
}
public void red() {
graphics.setPaint(Color.red);
repaint();
}
public void black() {
graphics.setPaint(Color.black);
repaint();
}
public void magenta() {
graphics.setPaint(Color.magenta);
repaint();
}
public void blue() {
graphics.setPaint(Color.blue);
repaint();
}
public void green() {
graphics.setPaint(Color.green);
repaint();
}
}
I add it to a JPanel. The drawing itself works fine. Now I would like to implement an undo method. I thought I could just copy the current graphics2D object into a backup variable and on click of the undo-button I would replace the current object with the backup object. Unfortunately that didn't work.
I tried the following:
In the mousePressed method I assigned the current value of graphics to the backup variable.
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
graphicsBackup = graphics.create();
oldX = e.getX();
oldY = e.getY();
}
});
and in the undo method I tried to assign the reference of the backup variable to the original object.
public void undo() {
graphics = graphicsBackup;
repaint();
}
In my second try I worked with the AffineTransform Object. I called getTransform in the keyPressed method to get the current state and then setTransform in the undo method. That didn't work either. Do you have any suggestions how it could work?
SOLUTION: (added some additional methods for background etc.)
class DrawPad extends JComponent {
private Image image;
private Image background;
private Graphics2D graphics;
private int currentX, currentY, oldX, oldY;
private final SizedStack<Image> undoStack = new SizedStack<>(12);
public DrawPad() {
setDoubleBuffered(false);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
saveToStack(image);
oldX = e.getX();
oldY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
currentX = e.getX();
currentY = e.getY();
if (graphics != null) {
graphics.drawLine(oldX, oldY, currentX, currentY);
}
repaint();
oldX = currentX;
oldY = currentY;
}
});
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (image == null) {
image = createImage(getSize().width, getSize().height);
graphics = (Graphics2D) image.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
clear();
}
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
public void clear() {
if (background != null) {
setImage(copyImage(background));
} else {
graphics.setPaint(Color.white);
graphics.fillRect(0, 0, getSize().width, getSize().height);
graphics.setPaint(Color.black);
}
repaint();
}
public void undo() {
if (undoStack.size() > 0) {
setImage(undoStack.pop());
}
}
private void setImage(Image img) {
graphics = (Graphics2D) img.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setPaint(Color.black);
image = img;
repaint();
}
public void setBackground(Image img) {
background = copyImage(img);
setImage(copyImage(img));
}
private BufferedImage copyImage(Image img) {
BufferedImage copyOfImage = new BufferedImage(getSize().width, getSize().height, BufferedImage.TYPE_INT_RGB);
Graphics g = copyOfImage.createGraphics();
g.drawImage(img, 0, 0, getWidth(), getHeight(), null);
return copyOfImage;
}
private void saveToStack(Image img) {
undoStack.push(copyImage(img));
}
public void red() {
graphics.setPaint(Color.red);
repaint();
}
public void black() {
graphics.setPaint(Color.black);
repaint();
}
public void magenta() {
graphics.setPaint(Color.magenta);
repaint();
}
public void blue() {
graphics.setPaint(Color.blue);
repaint();
}
public void green() {
graphics.setPaint(Color.green);
repaint();
}
}
SizedStack:
public class SizedStack<T> extends Stack<T> {
private final int maxSize;
public SizedStack(int size) {
super();
this.maxSize = size;
}
@Override
public Object push(Object object) {
while (this.size() > maxSize) {
this.remove(0);
}
return super.push((T) object);
}
}