怎样实现图形撤消(undo)、编辑文字功能?(200分)

  • 主题发起人 主题发起人 easyzhou
  • 开始时间 开始时间
E

easyzhou

Unregistered / Unconfirmed
GUEST, unregistred user!
大家好,估计你们都用过windows的画笔程序。
我现在遇到两个问题,
问题1:
实现象画笔程序那样,在图形中增加文字编辑框功能。
问题2:
编辑图形时,实现撤消(undo)的功能。

希望各位帮帮忙,指点小弟怎样实现。
 
不太好做,要记录每一步的操作
 
高难度的东西,给分太低了!
 
我现在只能撤消(undo)一步,不知道你达不达的到你的要求!
 
只要能解决问题,分数太低,可以再加分。
希望各位多多想办法。
to:网络浏览器
能undo一步也可以。
 
记录每一步的操作,定义个结构,再定义个动态数组就行了
DOWN时开始记录,UP时停止,就是完整的一步了。
TYPE
步骤号
工具号
颜色
鼠标事件(起、降、MOVE)
坐标
。。。。

 
一步的话?那就用一个临时表。记录更改前的附表。
 
sendmessage(Handle,wm_undo,0,0);
不知道有沒有用﹗
 
定义结构,以前做过一个CAD的项目,它就是把
每一步操作定义成一种内部结构保存在临时文件
中的。
注意,Undo不是从最后一条倒推上去,而是从第一条再做到你Undo的那一条
 
解决问题1,  能给多少分
 
其实,很简单,我曾经的一个课程设计就是画图!
1、我的解决方案是:给他一个编辑框,让他输入汉字,当然,这个编辑框的位置是变化的,
大小也是可以变化的,这个实现也不复杂。然后,用户回车后,就用textout输出到鼠标指定的点。
2、设一个堆栈,就是一个记录数组,使用先进后出的算法,存放(起始坐标点,画笔类型、
画刷类型,颜色,结束坐标点),undo是就进行反操作。
 
to:弄影,我现在做的和你说的基本相同,不过我用的是Memo,
请问怎样控制编辑框的大小?
 
我当时的方法比较投机,没有用鼠标拖动定义编辑框大小,
而是,选定输入汉字时,定义编辑框大小(用户输入大小的数字),然后,
如果不够大,再根据font.size和length(text)改变编辑框大小

//你刚才是怎么做的,让我弹出那个框框
 
用一个双向单链表实现,
 
输出文字,我是用一个memo,再加两个按钮:"确定","取消"。
undo:我先把image保存起来。不知堆栈咋座的
 
看看我跟visual graphi作者的对画吧

Me: 看了你的作品,感觉上应该是很不错的,可为什么在市场上没什么听说,是市场操作不
够还是怎样,可惜,或是其它原因(我不是想投资,我是一个PR,只是有感于中国软件),
我是搞数据库的,但爱钻牛角尖,最近被一个问题困住了,想请教一下,就是关于图片的
无级undo的实现,听DFW上说要什么大量的action来实现,你是如何实现的,能轻轻点一下
吗?

He: 这个不难,做两个动态数组(一个是Undo,一个是Redo),每变化一次,就把变化的类型和
变化量加入这个数组,就行了.当然,为了完整,每次先要放一个START标志,然后再放一个END
标志.Undo的时候,反其道而行,并打开Redo的动态数组,Redo的时候,打开的是Undo的动态数
组.动态数组有一个是激活的.
中国软件的希望在和传统产业的相融上,个人的成果不重要。

me: 说起来容易,可是真正的要如何融合,好像没人能找到这条出路啊!

你的意思是说每次做什么操作记录起来,然后再反其道而为,好像不对吧,因为我看PS
里的比如一个大步骤可能要0.5s,可是在undo里却很快,听DFW上说是用堆栈做的,并且
如果你那种方法步骤越多,就无法实现直接到达哪步,而要一步一步undo了吗?如果有些
操作是随机的时候(如有些像素是随机计算的),你如何做操作记录呢?

he: 我说的动态数组,对了,其实就是堆栈.
你纪录的内容应该这样,比如,目标是多边形,移动了一个顶点,你只需要纪录该顶点移动
前的坐标.比如移动整个多边形,你只需要纪录移动的幅度值,直接跳到历史的某一步不难
,当然要一步一步Undo了,比起原始的操作来,Undo当然要快点(因为不需要复杂的计算和
判断).

me: 还是不太明白,如果能再让我跳出来的话,真是太感谢了!
如:
原图
经步骤1:花0.1s 存入动态数组数据1
步骤2:花0.5s 存入动态数组数据2
步骤3:花1.0s 存入动态数组数据3
步骤4:花1.5s 存入动态数组数据4

此时原图已经四步改变变成了图4了,就是如果要直接恢复到图1步状态,你
的意思是否是从图四一步一步恢复到图1呢,这样恢复花的消耗不是要后三步之和吗?就
是说要花1.5 + 1.0 + 0.5 = 3s吗?并且如果说记入每步操作前的信息好像也不太合适
吧,如果一个图很大,那么一次记录改变了其所有像素的值,总不可能把这些像素改变
前的值放入数据中吧,这样动态数组不是会太大了,我想PS在做这个的时候应该只是将
操作类型放入其中吧,比如说只记录些次操作的名称,更改值等吧,不可能将改前的值
放入吧,并且看DFW们的讨论,PS在5.0还没实现无级undo功能,在6.0才实现,应该经过
很复杂的数据压缩才能做到你所说的那种堆栈来做吧,是不是?
按照你看将来Java + Linux有前途吗?我正在想是学J2EE还是J2ME,给个建议,OK

he: Photoshop在Undo的时候很快吗?我不知道?如果是,估计是把整个图像保存了.如果
只是保存步骤的话,应该是三秒才对(当然要免除在屏幕上画图的过程,在内存画图)

多学点知识,不局限于JAVA+LINUX,要了解多种技术,知道各种技术的优缺点.

me: 想啊,可是一个人的东东是有限的,我比起周围的人很努力的学了,但还是难啊

ps的历史路径很快,你应该有ps吧,拿来试试就知道了,如果整个图保存,那不可
能,如果操作一百步不就要保存一百个图了,可我看ps好像没有添加什么资源占用,应
该不是这样实现的,好了,不打搅你了,谢谢了这几次的讨论,如果你找到更好的方法
,请告诉我一声,祝好运,新年快乐啊!

还没完。。。

希望张兄不介意在下贴出来

 
备注:张兄是我写过信的回复最快,最热心的一位,非常之感谢,忠心祝他好运,:)
说实话,不像有些人(当然也许他真的很忙),不过我想如果他能认真一点多回几个字,
我会很感谢的:(
 
第2个问题,其实也就是保存当前图形的问题
至于保存方式, 如果要求undo的步骤不多, 可以直接在内存将bitmap保存,
如果要求无限次的undo,恐怕只好保存在硬盘文件中了
我曾经这样作过, 下面是代码, 呵呵可能不全看代码可以理解大部分的意思:

public
{ Public-Deklarationen }
sCurrentFile: string;
sHistory : array [0..1001] of string; //
nCurrentOperationSN : integer; //当前动作的序号, 用于记录当前历史记录
nMaxOperationSN : integer; //当前动作的序号, 用于记录当前历史记录

inFile : text;


procedure SaveCurrentBitmap(myBitmap: TBitmap; sCurrentFile : string; sCurrentOperationName: string); //保存历史操作
function LoadLastBitmap(var myBitmap: TBitmap; sCurrentFile : string; var sCurrentOperationName : string): boolean; //载入指定历史图片, 返回前一个操作名 ,undo不成功时返回false
function LoadNextBitmap(var myBitmap: TBitmap; sCurrentFile : string; var sCurrentOperationName : string): boolean; //载入指定历史图片, 返回下一个操作名
procedure TFormMain.undo1Click(Sender: TObject);
var
bmp : Tbitmap;
sTemp : string;

begin
bmp := TBitmap.Create;
// bmp.Assign(image1.Picture.Graphic);
if LoadLastBitmap(bmp, sCurrentFile, sTemp ) then //载入指定历史图片, 返回当前操作名
begin
image1.Picture.Bitmap.Assign(bmp);
if nCurrentOperationSN=0 then
MainMenu1.Items.Items[1].Items[0].Caption :='撤消 '
else
MainMenu1.Items.Items[1].Items[0].Caption :='撤消 '+sHistory[nCurrentOperationSN];
end;
MenuProcess;
end;

procedure TFormMain.mnuNextStepClick(Sender: TObject);
var
bmp : Tbitmap;
sTemp : string;
begin
try
if nCurrentOperationSN = nMaxOperationSN-1 then
begin
Exit;
end;
bmp := TBitmap.Create;
if LoadNextBitmap(bmp, sCurrentFile, sTemp ) then //载入指定历史图片, 返回当前操作名
begin
image1.Picture.Bitmap.Assign(bmp);
// inc(nCurrentOperationSN); //
MainMenu1.Items.Items[1].Items[0].Caption :='撤消 '+sHistory[nCurrentOperationSN];
end;
MenuProcess;
except
end;
end;

procedure TFormMain.undo1Click(Sender: TObject);
var
bmp : Tbitmap;
sTemp : string;

begin
bmp := TBitmap.Create;
// bmp.Assign(image1.Picture.Graphic);
if LoadLastBitmap(bmp, sCurrentFile, sTemp ) then //载入指定历史图片, 返回当前操作名
begin
image1.Picture.Bitmap.Assign(bmp);
if nCurrentOperationSN=0 then
MainMenu1.Items.Items[1].Items[0].Caption :='撤消 '
else
MainMenu1.Items.Items[1].Items[0].Caption :='撤消 '+sHistory[nCurrentOperationSN];
end;
MenuProcess;
end;

procedure TFormMain.SaveCurrentBitmap(myBitmap: TBitmap; sCurrentFile : string; sCurrentOperationName: string);
begin
try
myBitmap.SaveToFile(ExtractFilePath(Application.ExeName)+ChangeFileExt(ExtractFileName(sCurrentFile),
'_'+IntToStr(nCurrentOperationSN)+'.bmp'));//
sHistory[nCurrentOperationSN] := sCurrentOperationName; //给操作历史命名
// showMessage(sHistory[nCurrentOperationSN]+' '+ intToStr(nCurrentOperationSN));
nCurrentOperationSN :=nCurrentOperationSN+1;
nMaxOperationSN := nMaxOperationSN+1;
if nCurrentOperationSN>1000 then nCurrentOperationSN:=1;
except
ShowMessage('保存临时文件出错,“历史列表回溯”功能失效'
+''#10#13'请退出程序,然后打开“文件”中的历史记录文件再试'); //
end;
end;
procedure TFormMain.DoSthForUndo(sThisAction :string); //为undo做准备
begin
if bIsRunMacro=true then exit; //如果现在是执行宏功能,则不做undo
SaveCurrentBitmap(Image1.Picture.Bitmap, sCurrentFile , sThisAction);
MainMenu1.Items.Items[1].Items[0].Caption :='撤消 '+sThisAction;
HistoryListBoxForm.ListBox1.Items.Add(sThisAction); //
MenuProcess;
end;


procedure TFormMain.Resample1Click(Sender: TObject);
begin
DoSthForUndo('图象大小调整');
ResizeCtrlForm:=TResizeCtrlForm.Create(Application);
try
ResizeCtrlForm.FSrcBitmap.Assign(Image1.Picture.Bitmap);
ResizeCtrlForm.ShowModal;
if ResizeCtrlForm.ModalResult=IDOK then
begin
Image1.Picture.Bitmap.Assign(ResizeCtrlForm.FDestBitmap);
Image1.Repaint;
end;
finally
ResizeCtrlForm.Free;
end;
end;

procedure TFormMain.Rotate1Click(Sender: TObject);
begin
DoSthForUndo('图象旋转'); //
RotCtrlForm:=TRotCtrlForm.Create(Application);
try
RotCtrlForm.SrcBitmap.Assign(Image1.Picture.Bitmap);
RotCtrlForm.ShowModal;
if RotCtrlForm.ModalResult=IDOK then
begin
Image1.Picture.Bitmap.Assign(RotCtrlForm.DestBitmap);
Image1.Repaint;
end;
finally
RotCtrlForm.Free;
end;
end;

 
通过对ps的在temp文件中的跟踪:
一进入ps:生成一个29.3M的文件
打开一2.8M的文件,文件增长到39.nM
操作一步,文件增长3M
操作一步,文件增长3M
操作一步,不增长
操作一步,增长3M
操作一步,不增长
操作一步,增长3M
操作一步,不增长
操作一步,增长3M
看来ps也没什么高招了,吃空间大户啊
 

Similar threads

D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
776
DelphiTeacher的专栏
D
D
回复
0
查看
824
DelphiTeacher的专栏
D
D
回复
0
查看
632
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
后退
顶部