0

I am currently working on a paint program and had implemented undo & redo methods, but as my program was slowing down as the user drew, I decided to not store the co - ords in an ArrayList, but directly apply them to a BufferedImage. The problem with this method is that I cannot undo or redo as I used to. I have searched around on the interweb, and found a reference to a class called UndoManager. I have no idea how to apply it to a graphics object, though (given my condition).

I can still store the co-ords, but that is useless to me as I draw them on directly, unless anyone could think of a way to overlay a black colored line w/ a transparent one to replace it (Currently, for that purpose I use BufferedImage.setRGB(x,y,color), but that only helps when the shape I want to replace is a circle (x^2 + y^2 = r^2) or rectangle).

Any ideas?

gedr
  • 316
  • 1
  • 2
  • 12
  • How are you creating the ArrayList? Have you tried using a LinkedList? – MadProgrammer Feb 06 '14 at 20:51
  • @MadProgrammer I used to use an AL, but as I implemented the rubber tool (which has to use BuffImg.setPixel instead of G2D.drawLine due to the canvas being transparent) the performance went down drastically as I had to setPixel r*r*pi times, where r is variable, for each click or drag, including all the other strokes. – gedr Feb 06 '14 at 23:10
  • @MadProgrammer I just read this [**thread**](http://stackoverflow.com/questions/5846183/arraylist-vs-linkedlist?lq=1) and found out that LinkedList's are slower than AL's. What is the reason you ask? – gedr Feb 06 '14 at 23:13
  • 1
    `LinkedList` is slower if you want none sequential access, it is faster in it's growth capacity as it does not need to continuously reallocate the backing buffer, so it's not creating a lot of objects that need to be garbage collected. As I see it, you are only pushing and popping to the list and running a linear access (running from the start to end of the list) and `LinkedList` may actually give you better performance. – MadProgrammer Feb 06 '14 at 23:23
  • 1
    You could also reduce the concept of what an undo action does, so if the user presses the mouse button and does a dragging action, I would consider that until the mouse is released, that would constitute a single action. I might be easier to separate what you need to draw from what is considered a "undo"-able action - as an idea – MadProgrammer Feb 06 '14 at 23:24
  • Alright, I get what you mean (press right click, hold and press left click to cancel). It is do-able with events handling a flag that until is false collects co-ords, and if it goes through w/o interuptions, is drawn. However, this is not a pernament solution, i.e. I want a undo button for atleast 20 previous moves... I might save 20 bufferedImages every time the user has drawn in a queue, and take out the newest one as the user presses undo. I don't think it would be the most efficient solution, but at the moment I don't have any other ideas. You? - I'll read your reply in the morning. – gedr Feb 06 '14 at 23:46
  • Also, I probably won't be going to itirating arrays just to draw - it has good elements to it, i.e. you can take the latest line away, but is much slower than using BI's. Also, I finally got the scale tool AND rubber tool working when I switched over. It's an idea - i'll take it into consideration as a last resort ;). – gedr Feb 06 '14 at 23:55
  • @MadProgrammer Do you have any other suggestions other than saving the last 20 images, in a queue, which each image is added when mouseReleased? If yes, what? – gedr Feb 07 '14 at 18:49
  • Yes, pretty much anything else other then adding BufferedImages into a list, that's a considerable memory overhead. Personally, I'd be working on the principle of "commands" which can be "execute" and produce so e kind of result, like circle and square. This way you can ad each command to a list and reproduce the image, undo wild just be popping the last command of the list...but that's just me... – MadProgrammer Feb 07 '14 at 19:49

1 Answers1

2

You can keep a delta of what you modified in your last action, and when undoing apply the inverse of that delta.

This is what I mean: suppose you have an image A and you draw on top of it a line, so now you have image B. If you compute C = A xor B then C is the inverse of the delta you just did. So you just have to store C in order to make an undo. The undo process will be: A = B xor C.

Of course A, B and C need not be you complete image, they can be just the changed regions with some information of their starting point.

And if you want to redo, it's: B = A xor C

The logic behind this goes as follows, bear in mind that xor'ing a value with itself yields 0, and xor'ing any value with 0 yields the same number. I'll use ^ as short for xor as you are using Java.

If you have a image with 3 pixels in a row, with RGB values:

A = [ (255,255,255) , (255,255,255), (255,255,255) ]

and you draw on top of the middle pixel with red ink, you will get:

B = [ (255,255,255) , (255,0,0), (255,255,255) ]

Now, calculate the delta by performing the xor of the two images:

C = [ (0,0,0) , (0,255,255), (0,0,0) ]

If you want to get A from B and C you do B xor C which is:

[ (255 ^ 0, 255 ^ 0, 255 ^ 0) , (255 ^ 0, 0 ^ 255, 0 ^ 255), (255 ^ 0, 255 ^ 0, 255 ^ 0) ]

Which is the original image:

`[ (255,255,255) , (255,255,255), (255,255,255) ]`

Here I used 255 and 0 as those numbers have easy calculable xor's, but it works with any value. Try A = 127, B = 83 and calculate C as 127^83, and then check that A = B^C

Hope it helps.

Daniel
  • 21,933
  • 14
  • 72
  • 101
  • I'm sorry, but I have not worked with binary logic before, so could you explain this to me, and also (possibly, if you could) include code? Thanks. – gedr Feb 06 '14 at 20:16
  • I modified my answer to add a more detailed explanation. – Daniel Feb 06 '14 at 20:32
  • Given the fact that the OP was having an issue with the program slowing down by storing points in an ArrayList, I can only imagine that storing the byte delta between each image is only going to produce the same problem...but I do like the idea though ;) – MadProgrammer Feb 06 '14 at 20:49
  • Good point. It wasn't explicit but: the idea is saving each delta as an element of the ArrayList, instead of each point of the delta as an element. – Daniel Feb 06 '14 at 20:53
  • So, any other ideas guys? @MadProgrammer – gedr Feb 06 '14 at 23:16