Command Pattern for undo/redo in paint application

2019-03-16 16:21发布

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.

2条回答
疯言疯语
2楼-- · 2019-03-16 17:09

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.

查看更多
不美不萌又怎样
3楼-- · 2019-03-16 17:15

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

查看更多
登录 后发表回答