讨论一下关于图形处理软件中的撤消重复功能

mdejtod 2010-04-24 12:05:06
加精
RT。
举个例子,比如我新建了一个模板。模板里面可以添加无数个图层,每个图层都有N多个操作动作。
所有图层的操作都是串行进行的,现在要实现PS里面的撤销重复功能,如下图
步骤 0 1 2 3 4 5
类型 新增layer1 新增layer2 move layer1 删除layer2 新增layer3 move layer3
那撤销时,从第5个步骤往回一步步执行

我的实现方法是,先定义一个动作命令基类,再定义一些动作命令的派生类


type
TCommand = class
private
FLayerData : TData;//对应的图层数据结构体
protected
procedure DrawLayer; virtual; abstract;
public
procedure Execute;//在这个过程中执行 DrawLayer过程
end;
//移动
TCommandMove = class
private
FLayerData : TData;
protected
procedure DrawLayer; override;
public
end;
//重画
TCommandReDraw = class
private
FLayerData : TData;
protected
procedure DrawLayer; override;
public
end;


新建的模板也有一个对应的类
TComBine = class
private
FList : TList ; //用于添加动作类
FCommand : TCommand ; //动作命令类
FCurIndex : integer; //当前执行的操作列表下标
protected
public
property Items[Index : integer] : TCommand read SetItems write GetItem;
end;
每进行一个操作,就添加在TComBine 中添加一个具体的动作命令类对象,然后让动作命令类对象去执行相应的操作
撤销和重复时,只要修改FCurIndex 属性,然后用 TCommand(items[i]).Execute;
当有撤销操作时,再新增操作,直接将当前步骤之后的动作命令类对象全部销毁,类似于PS中的功能(撤销后再操作,当前步骤之后的操作就没了)

现在出来的问题:
比如从第5撤销到第4步时,它的操作应该是将 layer3 移到新建的位置,那我具体要怎么处理?直接执行第四步的话,它会创建一个LAYER,这显然不符合要求
又或者从第4撤销到第3步时,实际操作是删除layer3,直接执行的结果是将layer2删除

可能是我的处理模式本来就有问题,不知道大家有没有什么可行的方案可分享一下?不胜感激!!
...全文
2954 150 打赏 收藏 转发到动态 举报
写回复
用AI写文章
150 条回复
切换为时间正序
请发表友善的回复…
发表回复
landy2017 2011-11-02
  • 打赏
  • 举报
回复
有没有画图的实例呢?
landy2017 2011-11-02
  • 打赏
  • 举报
回复
太深了,这水;
C++,真不容易;
mdejtod 2010-05-04
  • 打赏
  • 举报
回复
代码就不贴了,跟 weiym 兄弟的是一样的,原理就是(暗思透竹) 兄弟所说,具体的操作还是比较复杂的
fishyu10 2010-04-30
  • 打赏
  • 举报
回复
想学习这个,有这种图层,图片处理的入门例子吗?
spirit308 2010-04-29
  • 打赏
  • 举报
回复
学习了谢谢
mdejtod 2010-04-29
  • 打赏
  • 举报
回复
to hustleverpi :
对autocad 软件不熟悉,但对你这句话 但是,如果手工去维护这个命令,手工去往栈中添加命令,往往使得编码的复杂度相当的高,特别是对于多个实体有联系的时候,更为复杂,深有同感,我就是用手工的方式去维护命令队列,现在麻烦的地方来了,当图层移动时,很多其它的图层的INDEX受到影响,我必须同时更新每个命令中的对象接收者(图层),同时还要更新相应的图层数据存放顺序
整个设计模式是这样的
TCommand = class
FinitData,FInallyData : PLayerData; //命令的初始和结果数据
FLayer : TRotLayer ; //命令对象接收者
FLayerIndex : integer; //当前对象接收者的INDEX
end;
如果我在外部,修改了某个layer的INDEX,那其它的图层也会受影响,这时就要更新命令队列中所有的对象接收者,不然执行 undo redo时,不能将操作正确的应用到对象接收者身上
不知道哪位可以提供方便可行的方法??不至于操作这么复杂!!
touch_hand 2010-04-29
  • 打赏
  • 举报
回复
用了N年PS才明白它为什么需要做一个“历史记录”窗口,还有为什么这么庞大,原来是有些计算不可逆的原因
hustleverpi 2010-04-29
  • 打赏
  • 举报
回复
最简单的实现方式,当然是GOF设计模式中的“Command”模式了。但是,如果手工去维护这个命令,手工去往栈中添加命令,往往使得编码的复杂度相当的高,特别是对于多个实体有联系的时候,更为复杂。
AutoCAD可以说是图形软件中最牛叉的了,从它提供的二次开发接口(ObjectARX)来看,它对Undo/Redo这方面封装的比较好。先不考虑它的具体实现机制,从表象上来看:
1、它的每个对象都是有读和写两种打开方式(一个ID对应一个实体,可以用读或者写的方式打开它);
2、对象的每个函数中,都必须添加一个assertReadEnabled函数(如果该成员函数对该对象为读操作)或者assertWriteEnabled函数(如果该函数对该对象是写操作)。如果当前对象是以读方式打开,但是调用的函数中遇到了assertWriteEnabled,则运行期错误发生;
3、AutoCAD内部,根据assertWriteEnabled实现UndoRedo,最基本的方式,我认为就是:在一个具体的命令开始之前,创建一个GOF的Command模式的对象(Commander),如果一个对象被以写方式打,并且调用了assertWriteEnabled,则备份一份,放入Commander中。Commander中的修改记录也是以栈的方式记录。当命令结束时,将该命令的Functor放入整体的Undo/Redo栈。
4、可以修改assertWriteEnabled的实现机制,避免整体复制,使其粒度更小。即采用对象局部更新的方式。
5、添加、删除图元,本质上就是对整体数据库这个比较大的对象的修改,也是采用assertWriteEnabled的方式实现。

对Arx的理解可能有偏差,如果本人表述有误,请谅解~
mdejtod 2010-04-29
  • 打赏
  • 举报
回复
[Quote=引用 145 楼 lanzhengpeng2 的回复:]
引用 31 楼 mdejtod 的回复:
to weiym
具体代码我没看,在设计自己的动作命令类,不过具体操作起来可能要比你的更复杂得多
涉及的方面有,新增,删除,移动,旋转,透明度,边框,文字,掩码图效果,画笔等等...而且是所有图层串在一起的操作.
大概看了一下,还是觉得受益非浅。现在的设计模式就跟你和CoderPlusPlus说的一样,太感谢你们两位了!!!,

最大的问题是,……
[/Quote]
一个人做,所以觉得很烦燥,问题一个个的冒出来,解决的方式也越来越觉得复杂
dungeonsnd 2010-04-29
  • 打赏
  • 举报
回复
要不然WORD反应怎么那么慢呢!
反应太慢令人难以接受,所以还不如少一些Undo的次数了。
要是能搞到PS这一段代码就好了,呵呵。。 不过可以针对软件图形的规模选择不同的方法吧。
dungeonsnd 2010-04-29
  • 打赏
  • 举报
回复
有意思,不过我目前还没遇到过,有空想研究研究。
楼主可以的话把完工的代码贴上来大家分享研究一下。

一开始我是用位图来保存不同状态下客户区的,因为窗口要刷新嘛,正好把它搞到内存DC里。但是这种方法速度太慢,而且占内存。

目前我也是做关于图形方面的软件,有空再研究喽。。。
lanzhengpeng2 2010-04-29
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 mdejtod 的回复:]
to weiym
具体代码我没看,在设计自己的动作命令类,不过具体操作起来可能要比你的更复杂得多
涉及的方面有,新增,删除,移动,旋转,透明度,边框,文字,掩码图效果,画笔等等...而且是所有图层串在一起的操作.
大概看了一下,还是觉得受益非浅。现在的设计模式就跟你和CoderPlusPlus说的一样,太感谢你们两位了!!!,
[/Quote]
最大的问题是,某些操作是不存在相反的操作,这类操作只能局部保存状态做为Undo的结果.
具体实施的时候,有的操作又要持续一段时间才能完成的,比如涂抹,喷雾,因此,局部保存的状态需要不停叠加,最后算到一个Undo操作里。
最后需要的UndoStack,UndoRecord,UndoAtom。
原理如发布代码的那个哥们,实际情况会复杂很多。最大问题不在于基础设计,而在于操作众多,而每个操作都需要MakeCommand,Execute,Undo,太痛苦了。如果你有小弟帮着做事还好些——我就是有几个小弟,但我看着他们写我都觉得痛苦
laoduzhe2010 2010-04-28
  • 打赏
  • 举报
回复
比较关注,帮你顶,期待一下高人
wenshuanglin 2010-04-28
  • 打赏
  • 举报
回复
学习了
zhiguanglight 2010-04-28
  • 打赏
  • 举报
回复
收藏了
doudoullihaijun 2010-04-28
  • 打赏
  • 举报
回复
顶vvvvvvvvvvvvv
satyer 2010-04-28
  • 打赏
  • 举报
回复
学习中,也受益中!
PhotosShop 2010-04-28
  • 打赏
  • 举报
回复
photoshop中不会用保存到临时文件来处理撤销和重做的,因为如果被修改的对象是图像数据本身,如果保存到文件,为保证图像数据不丢失,要么不压缩,直接保存为BMP格式,要么采用无损压缩,第一种方法,保存速度快,可对硬盘的写入量过大,对硬盘不利,采用第二种方法,压缩占用时间,无法满足PS的速度要求。

photoshop中可撤销重做的步数是有限的,并且即使对于图像他也不是采用逆向算法来实现撤销的,一是因为PS重所有的算法严格意义上都不具有逆向算法的,即使像反色这样的命令也是(带羽化效果选区的反色),二是因为即使有逆向算法,很多算法的复杂性是的撤销和重做相当缓慢,这是用户所不能容忍的。所以PS直接是用的内存,保存了他需要记录的数据。

图像处理中可撤销的种类异常繁多,比如:选区、滤镜、添加图层、移动图层、更改图层属性、合并图层等等。比起一般的软件的撤销对象比较单一,图像中的撤销更需要注重速,而起复杂性也就不言而喻。

sino_crane 2010-04-28
  • 打赏
  • 举报
回复
看看看看
zhanshannao62 2010-04-28
  • 打赏
  • 举报
回复
哇哈哈
加载更多回复(130)

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi GAME,图形处理/多媒体
社区管理员
  • GAME,图形处理/多媒体社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧