第一次在大富翁论坛上提问,就成为热点,我好高兴!最重要的是认识了不少高手。 (111分)

  • 主题发起人 主题发起人 wwolf
  • 开始时间 开始时间
to aizb:
多谢提醒,我一直想不起这个写法。 :(
 
新的一天,新的开始,今天又将有哪位高手来临?
 
看到老是有人问DirectDraw抓屏幕的问题,也没人来解答,是不是很多大虾不愿意把方法
告诉新手啊?下面贴上程序,用的是标准的DirectX7.0的接口,不用DelphiX,那个用起来
太麻烦,效率也低。不过注意了,采用DirectDraw来Capture Screen可不会象你们想的那么
快速,实际上和GDI差不多,因为GDI的Bitblt和DirectDraw在最后实现上都是相同的。

注意需要 uses DirectDraw;这个单元可以在 Jedi-GraphX 找到。
delphi6 的第2张盘上也有。

全局变量:
Var

Draw7:IDirectDraw7;
PriSurface7,
BakSurface7:IDirectDrawSurface7;


OnFormCreate 时初始化

Var Rs:HResult;
Desc:TDDSURFACEDESC2 ;
PF:TDDpixelFormat;
Begin
Rs:=DirectDrawCreateEx(nil,Draw7,IID_IDirectDraw7,nil);
IF Rs<>DD_OK Then Exit; //DirectDraw7初始化失败
Rs:=Draw7.SetCooperativeLevel(Handle,DDSCL_NORMAL );
IF Rs<>DD_OK Then Exit; //DirectDraw7设置协作级别失败

// 下面建立主表面
FillChar(Desc,sizeof(desc),0);
Desc.dwSize := Sizeof(desc);
Desc.dwFlags := DDSD_CAPS;
Desc.ddsCaps.dwCaps:=DDSCAPS_PRIMARYSURFACE;
Rs:=Draw7.CreateSurface(Desc,PriSurface7, nil);
IF Rs<>DD_OK then Exit; // 建立主表面失败

// 下面建立内存缓冲表面
Desc.dwFlags := DDSD_CAPS OR DDSD_HEIGHT OR DDSD_WIDTH OR DDSD_PIXELFORMAT ;
Desc.dwWidth := Screen.Width;
Desc.dwHeight:= Screen.Height;
Desc.ddsCaps.dwCaps:=DDSCAPS_OFFSCREENPLAIN OR DDSCAPS_SYSTEMMEMORY ;
//定义像素格式为32bit,你也可以自己定义,参看msdn 相关内容
FillChar(PF,Sizeof(PF),0);
PF.dwSize:=Sizeof(PF);
PF.dwFlags:=DDPF_RGB;
PF.dwRGBBitCount:=32;
PF.dwRBitMask:=$FF;
PF.dwGBitMask:=$FF00;
PF.dwBBitMask:=$FF0000;
PF.dwRGBAlphaBitMask:=$FF000000;
Desc.ddpfPixelFormat:=PF;

Rs:=Draw7.CreateSurface(desc,BakSurface7, nil);
IF Rs<>DD_OK then Exit; //建立缓冲表面失败

BakSurface7.PageLock(0); //启动DMA能力(如果可能的话)

//至此初始化完成
End;


抓屏幕过程

假设输入指针 myPtr 是你自己处理的位图指针 ,注意你的指针必须已申请内存,且大小
能够容纳你开始设置的满屏图象。

Var DSD:TDDSURFACEDESC2;
PS,PD:PDWord;
Y:Integer;
Begin

BakSurface7.Blt(nil,PriSurface7,nil,0,nil) ; //拷贝屏幕到后台缓冲

Fillchar(DSD,Sizeof(DSD),0);
DSD.dwSize:=Sizeof(DSD);
IF BakSurface7.Lock(nil,DSD,DDLOCK_SURFACEMEMORYPTR,0)=DD_OK then
For Y:=0 to DSD.dwHeight-1 DO
Begin
PD:=Pointer(DWORD(DSD.lpSurface)+Y*DWord(DSD.lPitch));
PS:=Pointer(DWORD(myPtr)+(DSD.dwHeight-1-Y)*Dword(DSD.dwWidth)*4);
Move(PS^,PD^,DSD.dwWidth*4);
End;

BakSurface7.unLock(nil);

//到这里已获得屏幕
End;


OnFormClose 释放对象 注意不要写在 OnFormDestroy
Begin

BakSurface7.PageUnLock(0);
BakSurface7:=Nil;
PriSurface7:=Nil;
Draw7:=Nil;
End;

 
DirectDraw抓屏的速度和GDI差不多??
谁来说说,是不是这样啊??
 
原理很简单,主表面是显存,存在于显示卡上,从显示卡上向主内存拷贝数据是很慢的。
反之,从主内存向显示卡就很快。这也许是硬件的体系设计问题。
那些说抓屏很快的是有道理,但是抓到了要能读出来处理就很慢了。
你可以比较一下所有的软件,如 lotus ScreenCamera , HyperSnap ,还有最新的也是
Hyper 公司出的,没有一个快的 。

对了 上面的
PD:=Pointer(DWORD(DSD.lpSurface)+Y*DWord(DSD.lPitch));
PS:=Pointer(DWORD(myPtr)+(DSD.dwHeight-1-Y)*Dword(DSD.dwWidth)*4);
Move(PS^,PD^,DSD.dwWidth*4);
部分PS,PD写反了,要交换一下。
Move(PD^,PS^,DSD.dwWidth*4);


 
如果真是这样,那还不如用GDI好了。
对了GGCAT,你认为我这样做(题目上的步骤),可以吗。

在window系统消息中有没有一个是通知屏幕有改变要从重的?:)
 
1.用Bitblt抓屏到一个DIB中,将此DIB和上一幅XOR。 //没必要 反正还是要判断
2.将XOR后的异或图分块。 //需要
3.用Scanline对每块的像素进行比较。 //scanline 慢了
4.如果有某一像素的色值不是黑色则表示此小块有变动。
5.如果是都黑色,则表示此小块没有变动。
6.继续从第3步开始,直到所有小块比较完毕。
7.Sleep(15)延时15毫秒后,继续第1步开始。

应为

初始化各个小图块的地址偏移表,
申请一块内存BlockMem,大小为小图块的大小

输出序列信息,包括图象width,Height,子图块Width,Height

1) 获取当前画面,定位数据指针
2) 在小图块的地址偏移表中循环
A)把数据序列化到 BlockMem中,同时判断本小图块数据变化否?
B)若有变化,压缩 BlockMem ,输出块序号和压缩数据,否则继续循环
3) 送结束信号,延时,返回1)

注意,要做到线程里啊~

"在window系统消息中有没有一个是通知屏幕有改变要从重的"
没有,除非你hook整个系统的WM_Paint,WM_Erasebkgrd等消息。
结果会做很多无用的工作拖慢系统。因为消息可是每秒n多次啊,
随便缩放一下窗口就产生近百次或更多。而且一些的程序是
主动绘制的。他绘制了你更本不知道。要拦住这些操作,你就
要自己重载一大堆和绘制有关的 API,你知道有多少吗?就算你
是古今大高手,做到了,那DirectDraw你也得重载啊。还有OpenGL
的,哈,不说了。反正你知道是不可行的就行了。

 
有了你们的肯定,我的信心增强了不少。
不过,GGCAT,我还想:
(应为

初始化各个小图块的地址偏移表,
申请一块内存BlockMem,大小为小图块的大小

输出序列信息,包括图象width,Height,子图块Width,Height

1) 获取当前画面,定位数据指针
2) 在小图块的地址偏移表中循环
A)把数据序列化到 BlockMem中,同时判断本小图块数据变化否?
B)若有变化,压缩 BlockMem ,输出块序号和压缩数据,否则继续循环
3) 送结束信号,延时,返回1)

你说的这段话,能给一段代码吗?
比如:1.初始化各个小图块的地址偏移表
2.获取当前画面,定位数据指针
等等,我都不太明白。拜托了!!万分感激你。

我现在是用ZLIB进行压缩处理,好像很慢,有更好的吗??
 
哎~~~~ 懒得和你扯了 ,还要源码。。。。。[:(][:(][:(][:(]
 
OK,OK,GGCAT,谢谢你啦,
卷起千堆雪tyn,谢谢你的肯定
其它各位朋友,谢啦
 
ScanLine一行一行的扫描,如果该行没有变化,直接跳转到下一行;
速度绝对快.
其实很多的图象处理算法都是采用扫描线来处理的,反而BitBlt是采用逐字节进行,故而速度慢.


我怎么觉得问题已经有了明确的答案啊,这位朋友还想怎样?
为了表示鼓励,将本帖子加为热点,RE一把.
 
为什么还有两个回复我看不到?
 

GGCAT 你的LZW单元我怎么用不起来?老是出错..
 
ggcat你好,你上面的DirectDraw抓屏幕例子,怎样将抓到的屏幕放到image中?

hlzee@163.com
 
后退
顶部