18

我正在研究如何编写一个支持撤消的绘制程序,并看到很可能命令模式是我想要的。不过,有些事情仍然让我无法理解,我希望有人可以提供一个简单的答案或确认。

基本上,如果我要体现撤消命令的能力,例如在屏幕上标记一个实心圆圈,这是否意味着我需要将圆圈覆盖到内存中的帧缓冲区复制到这个命令对象中?我看不出有任何其他方法可以撤消可能发生的事情,例如,在一堆随机像素颜色上加盖印章。

我听说一种方法只是跟踪前向操作,当执行撤消时,您只需从第 1 步开始并向前拉到撤消前的步骤,但如果您要支持大型操作,这似乎不可行撤消堆栈。

也许解决方案介于您保留每 15-20 个动作的位图并从最后一个“保存”转发开始之间。

有人可以提供有关在这种情况下典型接受的方法是什么的任何见解,或者在命令中保存缓冲区矩形,重做每个动作,或者我完全错过了什么?

更新:很多很好的回应。谢谢大家。我正在从我正在阅读的内容中考虑,我将通过每 N 个操作保存缓冲区来解决此问题,并且当用户发出撤消命令时,从最近保存的缓冲区中重做所有命令。我可以将 N 调整到尽可能高的值,不会明显影响需要响应式撤消的用户体验(以最大限度地减少内存使用),但我怀疑在这一点上没有真正确定,我应该是能够在一帧中执行相当多的动作,这还不算太糟糕。希望这种方法能让我快速确定是否转向另一个方向,而是为需要它的操作保存先前状态的位图矩形。

4

5 回答 5

10

首先,小心过度设计:如果您的应用程序不复杂且图像很小,您可能会发现“只存储所有内容”是快速、便宜且可行的。但假设不是这样:

您是正确的,从第 1 步开始为每个撤消重新绘制整个画布是不可行的;除非您的绘图程序非常简单,否则某些操作需要的时间太长。此外,可能不需要无限的撤消缓冲区(并且存储起来可能非常占用空间)。

如果你的艺术项目很复杂,我实际上会从混合方法开始,以处理各种操作。每隔一段时间保存一次帧缓冲区(您建议的每 15-20 个命令似乎没问题;我可能会从 10 个开始,一旦我工作了就进行调整)并从上次保存继续。但是不要让“每 15 次操作”变得死板,因为一些额外的经验法则可能会让用户看起来更加流畅。

例如,一些耗时或难以反转的操作总是会创建一个新的保存点:
- 任何画布调整大小(裁剪等)
- 任何保存。(“我刚刚保存”是用户很可能撤消回的地方。)
- 任何非常耗时的操作都应该在操作之后而不是之前创建一个新的保存点;即它应该标记下一个操作以保存缓冲区以撤消。(为什么?如果操作需要 30 秒,您希望之后堆栈中的每个撤消都需要额外的 30 多秒。)
- 相反,任何具有易于执行的数学负片或自反转(如光负片)的操作都无需费心保存帧缓冲区,也不应计入下一次保存。

所有这些都忽略了层的问题。如果你的程序有它们,显然只保存那些改变的层就足够了。

绝对是我的最高优先级建议:无论您使用什么方法,您都应该始终为最近执行的操作保存帧缓冲区。“糟糕,不是那个意思”是撤消的最可能原因,因此您总是希望撤消一步能够响应。如果它不是您保留的缓冲区,则可以在下一个命令执行后丢弃此缓冲区。

您还需要考虑什么构成了一个原子撤消操作。(例如,一个笔刷工具的一组笔画是一个操作还是多个操作?两者都有优点和缺点。)

于 2010-10-15T17:55:34.787 回答
3

也许解决方案介于您保留每 15-20 个动作的位图并从最后一个“保存”转发开始之间。

我会选择这样的东西。无论如何,您必须在某个时候绑定您的命令堆栈,因此如果用户清空它,您将需要一个起点。

当您到达边界并将其用作保存点时,您可以变得聪明并保存缓冲区,因为无论如何您都必须从堆栈中删除命令。本质上,您的保存点缓冲区是已删除操作的表示,因此当您从撤消堆栈中删除操作时,您只需将它们写入该缓冲区。

于 2010-10-15T17:34:50.730 回答
2

我听说一种方法只是跟踪前进操作,当执行撤消时,您只需从第 1 步开始,然后向前拉到撤消之前的步骤

这不是一个好主意。用户通常只撤消一些最近的操作,并且他们希望它很快,因此能够立即恢复比从一开始就重做所有事情要好。

有人可以提供有关在这种情况下典型接受的方法是什么的任何见解,或者在命令中保存缓冲区矩形,重做每个动作,或者我完全错过了什么?

您不必以相同的方式存储所有命令。根据操作的类型,您可以使用一种或多种技术,例如:

  • 绘图/绘画操作通常无法直接还原,因此您只能保存原始图像内容。但是,您可以通过仅存储已更改的部分图像而不是整个图像来节省空间。

  • 一些像反转颜色这样的操作本质上是可逆的,所以在这种情况下,您只需将操作的类型存储在撤消堆栈中,您可以在任一方向上重放操作。

于 2010-10-15T17:55:36.543 回答
1

如果您可能不会绘制巨大的位图,那么您的方法似乎完全可以。

为了更简单,将整个图片写入磁盘的 tmp 目录,看看它对用户来说会是什么样子。

不要一开始就过度设计——毫无疑问,还有其他问题需要解决。

于 2010-10-15T17:39:15.553 回答
0

据我了解,用于实现撤消/重做类系统的命令模式只是将操作记录在堆栈中,而不是这些操作的实际结果(因为这些操作将按顺序重新创建/删除)。我认为您提到了这一点,但说您认为大型撤消堆栈不可行。你可以说得更详细点吗?我相信这是可能的。

于 2010-10-15T17:35:21.863 回答