7

这个问题可能适用于一般的绘图系统。我想知道如何在 PS 中实现撤消功能。程序是否在每次操作之前拍摄画布的快照?如果是这样,这不会导致巨大的内存需求吗?我已经研究了命令模式,但我不太明白这将如何应用于绘图。

问候,门诺

4

5 回答 5

12

它被称为命令模式。它很容易实现,对任何类型的编辑器都很有用。

Photoshop 在原始图像上应用堆叠变换。一操作一命令。当您撤消时,它只是取消应用转换。所以它只保留原始版本和最新版本,但我想它可能会缓存最后几个版本只是为了提高性能。

于 2009-04-04T13:04:11.120 回答
4

由于某些操作将是不可逆的,并且正如您所说,每次都对整个图像进行快照是不可能的,那么我能看到的唯一其他选择就是一堆增量。delta 是一组掩码,包含在操作之前修改的像素。当然,许多操作可能是可逆的,因此可以优化它们的增量。

于 2009-04-04T13:04:29.183 回答
4

我不确定 Adob​​e Photoshop 是如何实现的,但Apple Shakeundo合成应用程序中的 Paint 节点很容易解释:

  • 每个笔触都存储为一系列点,以及一些信息,如笔触颜色、画笔大小等。
  • 绘制描边时,会在当前图像上进行更改。
  • x笔画(我认为是 10 笔)当前图像都会缓存到内存中。
  • 当您撤消时,它会重新绘制上一个缓存图像的最后约 9 次。

这有两个问题:

  • 当您撤消超过 10 次时,它必须重新计算整个图像。对于数千个笔划,这可能会导致几秒钟的暂停。
  • 使用 Shake,您可以保存包含笔画信息的设置文件,而不是实际的像素值。然后意味着每当您重新打开 Paint 节点或渲染图像时,您都必须重新计算整个图像(但问题几乎没有撤消操作那么大)。

好吧,还有第三个问题,Shake 是一个可怕的错误,并且在许多领域实现得很差,Paint 节点就是其中之一 - 所以我不确定这是一个多么好的实现,但我无法想象 Photoshop 是太不相似(尽管优化更好)。

于 2009-04-04T15:57:45.867 回答
0

我发现解决这个问题的最简单方法是使用持久数据结构,尽管我不知道 Adob​​e 是如何解决的,如下所示:

在此处输入图像描述

您将图像视为图像块的集合,例如每个 64x64 像素,它们会被垃圾收集或引用计数(例如:shared_ptr在 C++ 中使用)。

现在,当用户对图像拼贴进行更改时,您可以在浅层复制未修改的拼贴时创建一个新版本:

在此处输入图像描述

在这样的变化下,除了那些黑色瓷砖之外的所有东西都被浅浅地复制了。当你这样做时,你的整个撤消系统归结为:

before user operation:
    store current image in undo stack
on undo/redo:
    swap image at top of undo stack with current image

而且它变得超级简单,不需要在每个撤消条目中一遍又一遍地存储整个图像。作为用户复制和粘贴图层的奖励,除非/直到他们对粘贴的图层进行更改,否则几乎不会占用更多内存。它基本上为您提供了一个图像实例化系统。另一个好处是,当用户创建一个透明层,比如说,2000x2000 像素,但他们只绘制了一小部分图像,比如只有 100x100 像素,这也几乎不会占用任何内存,因为空/透明图块不会必须存储任何像素,只有几个空指针。它还加快了与此类大部分透明图层的合成速度,因为您不必对空图像图块进行 alpha 混合,而可以跳过它们。

至于PS动作,那是一种不同的方法。在那里,您可能会使用一些脚本来指示要执行的操作,但您可以将其与上述结合起来,以有效地仅缓存图像的修改部分。这种方法的重点是避免不得不一遍又一遍地深度复制整个图像,并炸毁内存使用以缓存图像的先前状态以进行撤消,而不必为各种类型的文件编写单独的撤消/重做逻辑。可能发生的不同操作。

于 2018-01-05T14:32:40.690 回答
-2

Photoshop 使用历史记录来跟踪他们的操作。这些也用作撤消,因为您可以随时回到历史。您可以在首选项中设置历史记录的大小。

我还建议您将Adob​​e Version Cue 作为一种用于追溯撤消或版本的工具,它内置在套件中仅用于此目的。http://en.wikipedia.org/wiki/Adobe_Version_Cue

于 2009-04-04T13:20:52.263 回答