【问题标题】:Undo for a paint program撤消绘画程序
【发布时间】:2011-04-26 01:33:43
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 我的 0.02 美元:实现任何最容易编写和维护的方法,并从那里进行优化。尝试以这样一种方式设计您的代码,以便您可以在以后更改实现它的方式。您可能必须制作某种缓存/交换文件才能支持“无限”撤消。
  • 如果不清除画布的“脏”部分(WRT 撤消)并重做 all 绘图操作,我想不出一种方法来实现这一点从头开始,或者保留帧缓冲区的各种备份,并且只从那里重新绘制。
  • 除非你能想出你执行的任何绘图操作的数学负数,并应用它,只存储这些操作的用户输入(以及必须生成的任何随机种子)。这不适用于不透明的东西,但可能适用于最终不会在任何时候达到颜色限制的过滤器。

标签: command design-patterns paint undo


【解决方案1】:

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

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

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

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

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

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

        李>

      【讨论】:

        【解决方案4】:

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

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

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            猜你喜欢
            • 2016-09-20
            • 2014-06-29
            • 2015-03-30
            • 2014-01-16
            • 2011-09-11
            • 2011-05-21
            • 2012-06-24
            • 1970-01-01
            • 2017-07-19
            相关资源
            最近更新 更多