Bonjour à tous,

Je crée actuellement un éditeur d'image en Java...
Actuellement, il est possible de dessiner sur un panel avec la souris (comme on dessine sur une feuille avec un crayon)...Sur ce panel est dessinée une image qu'on peut éditer...Mon but final est de créer une sorte de "Paint"...

Avant d'aller plus loin, je me suis dit qu'il serait bon d'implémenter les actions "Undo" et "Redo"...et c'est à ce niveau que j'ai actuellement un problème...

Mon éditeur se compose actuellement de 3 classes :

  • DrawPanel.java

    Cette classe est un panel sur lequel on dessine des images (BufferedImage)...Les 2 champs principaux de cette classe sont :

    - "historic" : une ArrayList contenant des BufferedImage. ce champ permet de garder l'historique des modifications faites sur l'image.

    - "currentIndexImage" : index dans l'ArrayList de l'image courante .


  • ImageEditor.java
    Cette classe est une JFrame contenant le DrawPanel (voir ci-dessus), un JLabel donnant les coordonnées de la souris dans le DrawPanel, et les 2 boutons "Undo" et "Redo"...


  • Main.java
    Permet de lancer le programme


Afin de cerner mon problème, je vous demande de lancer l'éditeur, de dessiner 3, 4 traits et d'utiliser les boutons undo et redo...Vous constaterez rapidemment qu'il est impossible de revenir à l'image initiale...et que lorsqu'on se trouve sur la dernière image, il faut cliquer deux fois sur "Undo" pour enlever la dernière modification...

A première vue, ca a l'air d'un compteur qui s'incrémente ou se décrémente mal, mais je ne vois pas d'où vient mon erreur! Je vous joins donc le code des 3 classes ainsi qu'une image de Test...

J'attends vos réponses avec impatience...Merci

DrawPanel.java
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
 
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
 
public class DrawPanel extends JPanel {
	private static int currentIndexImage = 0;
 
    // Historic of the images
    private ArrayList<BufferedImage> historic = new ArrayList<BufferedImage>();
 
    private Dimension preferredSize;
 
    /**
     * Constructors
     */
    public DrawPanel() {
 
    }
 
    public DrawPanel(ImageIcon image) {
    	BufferedImage newImg = new BufferedImage(image.getIconWidth(), image.getIconHeight(),BufferedImage.TYPE_INT_RGB);
    	Graphics g = newImg.createGraphics();
        g.drawImage(image.getImage(),0,0,null);
 
    	historic.add(newImg);
    	g.dispose();
    }
 
    /**
     * Set the new image to the panel and add it in the historic
     */
    public void setImage(BufferedImage image) {
 
    	BufferedImage newImg = new BufferedImage(image.getWidth(), image.getHeight(),BufferedImage.TYPE_INT_RGB);
    	Graphics g = newImg.createGraphics();
        g.drawImage(image,0,0,null);
        currentIndexImage++;
        historic.add(newImg);
        g.dispose();
 
    	//repaint();
    }
 
    /**
     * @return the image used for painting the background of this panel
     */
    public BufferedImage getImage() {
    	//System.out.println("Set Image Get = " + currentIndexImage);
    	return historic.get(currentIndexImage);
    }
 
    public BufferedImage getPrevImage() {
    	currentIndexImage--;
		return getImage();
	}
 
    public BufferedImage getNextImage() {
    	currentIndexImage++;
		return getImage();
	}
 
    public boolean isFirstImage() {
		return currentIndexImage == 0;
	}
 
    public boolean isLastImage() {
		return currentIndexImage == historic.size()-1;
	}
 
    public void setPreferredSize(Dimension pref) {
    	preferredSize = pref;
    	super .setPreferredSize(pref);
    }
 
    public Dimension getPreferredSize() {
    	BufferedImage img = historic.get(currentIndexImage);
    	if (preferredSize == null && img != null) {
    		//it has not been explicitly set, so return the width/height of the image 
    		int width = img.getWidth(null);
    		int height = img.getHeight(null);
    		if (width == - 1|| height == -1) {
    			return super .getPreferredSize();
    		}
    		return new Dimension(width, height);
    	} else {
    		return super .getPreferredSize();
    	}
    }
 
    /**
     * Overriden to paint the image on the panel
     */
    protected void paintComponent(Graphics g) {
    	super.paintComponent(g); 
    	Graphics2D g2 = (Graphics2D) g;
    	BufferedImage img = historic.get(currentIndexImage);
    	if (img != null) {
    		final int imgWidth = img.getWidth(null);
    		final int imgHeight = img.getHeight(null);
    		if (imgWidth == -1 || imgHeight == -1) {
    			//image hasn't completed loading, return
    			return;
    		}
 
    		Insets insets = getInsets();
    		final int pw = getWidth() - insets.left - insets.right;
    		final int ph = getHeight() - insets.top - insets.bottom;
 
 
 
 
    		Rectangle clipRect = g2.getClipBounds();
    		int imageX = (pw - imgWidth) /2  + insets.left;
    		int imageY = (ph - imgHeight) /2  + insets.top;
    		Rectangle r = SwingUtilities.computeIntersection(
    				imageX, imageY, imgWidth, imgHeight, clipRect);
 
    		if (r.x ==0  && r.y ==0 && (r.width ==0  || r.height ==0 )) {
    			return;
    		}	
    		//	I have my new clipping rectangle "r" in clipRect space.
    		//It is therefore the new clipRect.
    		clipRect = r;
    		//	since I have the intersection, all I need to do is adjust the
    		//x & y values for the image
    		int txClipX = clipRect.x - imageX;
    		int txClipY = clipRect.y - imageY;
    		int txClipW = clipRect.width;
    		int txClipH = clipRect.height;
   			g2.drawImage(img, clipRect.x, clipRect.y, clipRect.x
  					+ clipRect.width, clipRect.y + clipRect.height,
   					txClipX, txClipY, txClipX + txClipW, txClipY
   					+ txClipH, null);
    	}
    }
}
ImageEditor.java
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
 
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
 
public class ImageEditor extends JFrame implements  ActionListener ,MouseListener, MouseMotionListener
{
 /* Current mouse coordinates */
 private int mousex                = 0;
 private int mousey                = 0;
 
 /* Previous mouse coordinates */
 private int prevx                 = 0;
 private int prevy                 = 0;
 
 
 /* Initial state flags for operation */
 private boolean initialPen        = true;
 
 
 /* Primitive status & color variables */
 
 private Color  redColor          = new Color(255,0,0);
 
 
 /* Assorted status values for different variables */
 private JTextField mouseStatusBar  = new JTextField(10);
 
 
 /* Labels for operation and color fields */
 private JLabel cursorLabel         = new JLabel("   Cursor:");
 private JButton undoButton         = new JButton("Undo");
 private JButton redoButton         = new JButton("Redo");
 
 
 /* Sub panels of the main frame */
 private JPanel statusPanel         = new JPanel(new GridLayout(1,4,0,0));
 private DrawPanel drawPanel        = new DrawPanel(new ImageIcon("C:/smile.PNG"));
 
 private JPanel statusAndColorPanel = new JPanel(new GridLayout(2,1));
 
 private BufferedImage bi;
 
 public void init()
 {
	setTitle("ImagEditor v.1");
    setLayout(new BorderLayout());
    setSize(350, 350);
 
 
    /* Add label and cursor text field */
    statusPanel.add(cursorLabel);
    statusPanel.add(mouseStatusBar);
    statusPanel.add(undoButton);
    statusPanel.add(redoButton);
 
    // Add undo and redo buttons to actionListener
    undoButton.addActionListener(this);
    redoButton.addActionListener(this);
 
 
    /* Set not editable */
    mouseStatusBar.setEditable(false);
 
    statusAndColorPanel.add(statusPanel);
 
    // InteralFrame containing the panel displaying the image
    JInternalFrame maFrame = new JInternalFrame();
    maFrame.setVisible(true);
    maFrame.setLayout(new BorderLayout());
    JPanel centerPanel = new JPanel();
    centerPanel.add(maFrame);
	drawPanel.addMouseListener(this);
	drawPanel.addMouseMotionListener(this);
	maFrame.add(drawPanel,BorderLayout.CENTER);
	maFrame.pack();
 
 
	 // Place panel in the frame
    add(statusAndColorPanel, "North");
    add(centerPanel, "Center");
 
    this.setVisible(true);
    pack();
 }
 
 
/**
 * Draw a line from the previous mouse coordinates 
 * to the current mouse coordinates
 */
 private void dessinePen(Graphics g) {
	 g.drawLine(prevx,prevy,mousex,mousey);
	 g.dispose();
	 drawPanel.repaint();
 }
 
 /*
   Method will emulate a pen style graphic.
   by drawing a line from the previous mouse corrdinates
   to the current mouse coordinates.
 
   Note: In initial attempt the previous mouse coordinates
         are set to the current mouse coordinates so as
         not to begin the pen graphic from an unwanted
         arbitrary point.
 */
 public void penOperation(MouseEvent e)
 {
	 bi = drawPanel.getImage();
	 Graphics g = bi.createGraphics();
	 g.setColor(redColor);
 
	 // In initial state setup default values for mouse coordinates
	 if (initialPen)
	 {
		 setGraphicalDefaults(e);
		 initialPen = false;
		 dessinePen(g);
	 }
 
	 // Make sure that the mouse has actually moved from its previous position.
	 if (mouseHasMoved(e))
	 {
		 // set mouse coordinates to current mouse position
		 mousex = e.getX();
		 mousey = e.getY();
 
		 dessinePen(g);
 
		 // Set the current mouse coordinates to previous mouse coordinates for next time
		 prevx = mousex;
		 prevy = mousey;
	 }
 }
 
 /**
  * Implement actionPerformed method
  */
 public void actionPerformed(ActionEvent e) {
		if (e.getSource() == undoButton) {
			undoOperation();
		}
		if (e.getSource() == redoButton) {
			redoOperation();
		}
 }
 
 public void undoOperation() 
 {
	if (!drawPanel.isFirstImage()) {
		BufferedImage bi  = drawPanel.getPrevImage();  
		Graphics g = drawPanel.getGraphics();
		g.drawImage(bi,0,0,null);
		g.dispose();
		drawPanel.repaint();
	}
	else {
		System.out.println("Première Image");
	}
	//drawPanel.testAll();
 }
 
 public void redoOperation() 
 {
	 if (!drawPanel.isLastImage()) {
		 BufferedImage bi  = drawPanel.getNextImage();    
		 Graphics g = drawPanel.getGraphics();
		 g.drawImage(bi,0,0,null);
		 g.dispose();
		 drawPanel.repaint();
	 }
	 else {
		 System.out.println("Dernière Image");
	 }
 
 }
 /*
   Method determines weather the mouse has moved
   from its last recorded position.
   If mouse has deviated from previous position,
   the value returned will be true, otherwise
   the value that is returned will be false.
 */
 public boolean mouseHasMoved(MouseEvent e)
 {
    return (mousex != e.getX() || mousey != e.getY());
 }
 
 
 /*
   Method sets all the drawing varibles to the default
   state which is the current position of the mouse cursor
   Also the height and width varibles are zeroed off.
 */
 public void setGraphicalDefaults(MouseEvent e)
 {
    mousex   = e.getX();
    mousey   = e.getY();
    prevx    = e.getX();
    prevy    = e.getY();
 }
 
 
 /*
   Method will be activated when mouse is being dragged.
   depending on what operation is the opstatus, the switch
   statement will call the relevent operation
 */
 public void mouseDragged(MouseEvent e)
 {
    updateMouseCoordinates(e);
 
    penOperation(e);
 }
 
 
 /*
    Method will be activated when mouse has been release from pressed \
    mode. At this stage the method will call the finalization routines
    for the current operation.
 */
 public void mouseReleased(MouseEvent e)
 {
    /* Update current mouse coordinates to screen */
    updateMouseCoordinates(e);
 
    releasedPen();                 
 }
 
 
 /*
    Method will be activated when mouse enters the applet area.
    This method will then update the current mouse x and coordinates
    on the screen.
 */
 public void mouseEntered(MouseEvent e)
 {
    updateMouseCoordinates(e);
 }
 
 
 /*
   Method is invoked when mouse has been released
   and current operation is Pen
 */
 public void releasedPen()
 {
    initialPen = true;
    bi  = drawPanel.getImage();
 
    drawPanel.setImage(bi);
    //drawPanel.repaint();
 }
 
 
 
 /*
   Method displays the mouse coordinates x and y values
   and updates the mouse status bar with the new values.
 */
 public void updateMouseCoordinates(MouseEvent e)
 {
    String xCoor ="";
    String yCoor ="";
 
    if (e.getX() < 0) xCoor = "0";
    else
    {
       xCoor = String.valueOf(e.getX());
    }
 
    if (e.getY() < 0) xCoor = "0";
    else
    {
       yCoor = String.valueOf(e.getY());
    }
 
    mouseStatusBar.setText("x:"+xCoor+"   y:"+yCoor);
 
 }
 
 
 /*
   Method updates mouse coordinates if mouse has been clicked
 */
 public void mouseClicked(MouseEvent e)
 {
    updateMouseCoordinates(e);
}
 
 
 /*
   Method updates mouse coordinates if mouse has exited the frame
 */
 public void mouseExited(MouseEvent e)
 {
    updateMouseCoordinates(e);
 }
 
 
 /*
   Method updates mouse coordinates if mouse has moved
 */
 public void mouseMoved(MouseEvent e)
 {
    updateMouseCoordinates(e);
 }
 
 
 /*
   Method updates mouse coordinates if mouse has been pressed
 */
 public void mousePressed(MouseEvent e)
 {
    updateMouseCoordinates(e);
 }
 
 
}
Main.java
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
 
public class Main {
 
	/**
         * @param args
         */
	public static void main(String[] args) {
		ImageEditor sc = new ImageEditor();
		sc.setDefaultCloseOperation(ImageEditor.EXIT_ON_CLOSE);
		sc.init(); 
	}
}
L'image de Test