5

I would like to implement undo/redo in a small paint application. It seems the Command Pattern fits the use nicely, but I am unsure how to best implement it.

As I understand the pattern, it is necessary to include in each command:

  1. The details of the paint operation for purposes of redo (e.g. Line -> start & end points, free form line -> GeneralPath)
  2. The state of the component prior to the change for undo. In this case, that will be a small snapshot image of the area affected by the command.

My understanding based on that is that each command needs to be 'atomic' or self contained, with all the information needed for undo/redo that operation.

Unfortunately that would require storing more information than I'd first anticipated. For a line we must also account for things like the Color, Stroke and RenderingHints used to draw it initially. This turns my 'simple little commands' into something ..more bulky in memory, and with more boiler-plate code to churn out (each will be a serializable bean1).

For reasons of memory conservation (mostly) I was wanting to 'cheat' on the specification of the commands. Perhaps take a backup of the entire drawing area every 100th update, but otherwise store no part of the changed image, and simply rebuild the last (up to) 100 commands for each new paint operation. But that seems problematic to ensure that the state of the Graphics object is right before painting each part - this part might require a line, but the RenderingHints were changed 4 commands ago, the Color was changed 98 commands ago, while the Stroke has remained the same for the last 227 commands.

Pursuing a more memory efficient command seems to throw the pattern right out the window in terms of being 'atomic'. That in turn leads to difficulties in determining the earliest command that might affect the rendering.

Should I:

  • Look for a new pattern?
  • Attempt to implement my peculiar needs by tweaking the pattern?
  • Toss all this in the waste bin as premature optimization and code it in the simplest (and most memory consuming) way that sticks to the command pattern as defined?

Update

  1. "each will be a serializable bean" On 2nd thoughts, no. I did dome checks to find that a Graphics2D (which neatly encapsulates many parameters used when drawing) is not serializable. Further, a BasicStroke is serializable, but the thickness of the stroke is not stored. I could create serializable versions of many of the attributes but it seems to make for a lot more code, so I'm going to abandon that spec. as well. I will only attempt to store a reference to a BufferedImage at run-time.
Community
  • 1
  • 1
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 1
    Maybe you should use Memento pattern? – white Oct 08 '12 at 10:47
  • @white I have to look further into the [Memento pattern](http://en.wikipedia.org/wiki/Memento_pattern) but it seems the Memento object basically fills the role of the Command objects in the command pattern, and that each Memento would need to store the 'entire state' of the component prior to the change it refers to. So I'm thinking that leads me to the same problem of storing every operation atomically. – Andrew Thompson Oct 08 '12 at 10:53

2 Answers2

3

I would stick with command pattern and first try a naive solution (=the most memory-hungry). For some graphical operations it may be even necessary to keep a copy of the entire image in the command object (eg. think of filters). This is a common problem also in professional image editing applications, they often have a memory or step limit of last commands that are remembered. And if the memory consumption is really large you may think of swapping the oldest entries in command-history to file system. I think user will not mind waiting a second until the change is undone.

Adam Dyga
  • 8,666
  • 4
  • 27
  • 35
  • *"This is a common problem also in professional image editing applications,.."* That is an excellent point. If this problem becomes 'too hard' for the coders of a professional paint app. to implement, it seems I've got almost *no chance* of implementing it reliably. – Andrew Thompson Oct 08 '12 at 10:56
  • I wanted to leave this at least 24 hours before accepting ***any*** answer to encourage alternate view points (checks watch). The time limit has passed and I am guessing that 'no more answers' suggests people thought the 2 of you covered it. I choose your answer. While @stemm's answer provided some interesting ideas on optimizing the command pattern, the solution seems to come down to 'either implement the command pattern as it exists or prove you are more clever than some **extremely** clever people and invent a new pattern'. `` I'm not that clever. `` ;) – Andrew Thompson Oct 09 '12 at 12:49
  • @AndrewThompson don't be so humble ;) – Adam Dyga Oct 10 '12 at 09:03
1

Maybe, it would be better not to store copy of entire image in command, but store only copy of area, which is changing by command. Of course, this is not a panacea

stemm
  • 5,960
  • 2
  • 34
  • 64
  • *"but store only copy of area"* Yes, that is already under consideration ("small snapshot image"), and will be used in the final commands as I code them. Thanks for your reply. – Andrew Thompson Oct 08 '12 at 11:41
  • @Andrew Thompson, also, after watching your screenshot - I've concluded, that it would be efficient to do some lossless compression when you store snapshots – stemm Oct 08 '12 at 11:48
  • *"..would be efficient to do some lossless compression when you store snapshots"* Great idea. It will add a little CPU overhead but could save plenty on memory. – Andrew Thompson Oct 08 '12 at 11:51
  • @AndrewThompson you can even create an algorithm that calculates a set of minimal bounding rectangles (MBRs) that cover the changed area. This should make the size of the snapshot even smaller. – Adam Dyga Oct 09 '12 at 07:53