Delphi图像处理 -- 获取窗口或设备的图像数据

CSDN论坛中,经常看到有关截屏的贴。所谓截屏,指的是获取屏幕,或者屏幕上某个窗口上的信息,并将其转换为图像的操作。为此,也写了几个Windows下的“截屏”函数:

过程定义: // 获取Canvas有效区域内的图像数据。必须用FreeImageData释放数据结构 // 应调用ImageEmpty检查返回数据结构是否空 function GetImageData(Canvas: TCanvas; Rect: TRect): TImageData; overload; // 获取窗口有效区域内的图像数据。必须用FreeImageData释放数据结构 // 应调用ImageEmpty检查返回数据结构是否空 function GetImageData(Handle: HWnd; Rect: TRect): TImageData; overload; // 获取TGpGraphics对象有效区域内的图像数据。必须用FreeImageData释放数据结构 // 应调用ImageEmpty检查返回数据结构是否空 function GetImageData(g: TGpGraphics; Rect: TRect): TImageData; overload; 代码实现: procedure FillAlpha(Data: TImageData); asm mov edx, [eax].TImageData.Scan0 mov ecx, [eax].TImageData.Width imul ecx, [eax].TImageData.Height mov eax, 0ff000000h @PixelLoop: or [edx], eax add edx, 4 loop @PixelLoop end; function GetImageDataFromDC(DC: HDC; Rect: TRect): TImageData; var r: TRect; pbi: TBitmapInfo; begin FillChar(Result, Sizeof(TImageData), 0); if not GetDCClipBox(DC, Rect, r) then Exit; Result := GetImageData(r.Right, r.Bottom); pbi.bmiHeader := GetBitmapInfoHeader(Result); GetDCImageData(DC, r.Left, r.Top, Result, pbi); FillAlpha(Result); end; function GetImageData(Handle: HWnd; Rect: TRect): TImageData; var DC: HDC; begin DC := GetDC(Handle); try Result := GetImageDataFromDC(DC, Rect); finally ReleaseDC(Handle, DC); end; end; function GetImageData(Canvas: TCanvas; Rect: TRect): TImageData; begin Result := GetImageDataFromDC(Canvas.Handle, Rect); end; function GetImageData(g: TGpGraphics; Rect: TRect): TImageData; var Canvas: TCanvas; DC: HDC; begin Canvas := TCanvas.Create; DC := g.GetHDC; try Canvas.Handle := DC; Result := GetImageData(Canvas, Rect); finally Canvas.Free; g.ReleaseHDC(DC); end; end;

上面的代码提供了3个“截屏”方法,分别适合Delphi画布类TCanvas、窗后句柄和GDI+画布类TGpGraphics。

先简单介绍一下本文方法的实现原理:

3个“截屏”方法都是调用GetImageDataFromDC函数来完成的。

首先是通过Windows API CreateCompatibleBitmap建立一个与设备上下文DC兼容的位图句柄,然后调用Bitblt将有关设备的图像数据拷贝到位图中;

其次,需要把位图中的图像数据拷贝到TImageData类型的内存缓冲区。因为无论是显示屏、窗口还是其它设备,其分辨率是不尽相同的,就拿显示屏的显示质量来说,有设置为32位的,也有设置为16位的,甚至256色或更低质量的,要把它们转换为与之对应像素格式的图像,显然是很麻烦的(可参见《Delphi图像处理 -- 图像像素结构与图像数据转换》),必须把它们转换为一种统一像素格式的图像,以便进一步分析处理,好在Windows提供了这方面的API,即GetDIBits函数,可以很好的解决这个问题。内部过程GetHBitmapData就是调用API GetDIBits来完成该项工作的。过程中连续2次调用了GeiDIBits函数,第一次通过设置TBitmapInfo.bmiHeader.biBitCount为0,即像素格式为0后调用,此时GeiDIBits只是返回与位图句柄有关的信息,不会执行图像数据拷贝,根据返回的位图信息,我们准备好了接收图像数据缓冲区,从而第二次调用GeiDIBits,让GeiDIBits函数按照我们所需要的图像像素格式转换为图像数据,并拷贝到图像数据缓冲区TImageData.Scan0中,至此,“截屏”操作结束。

最后,由于Delphi图像处理系列,都是采用统一的32位图像像素格式,而GeiDIBits函数没有填充32位像素格式的Alpha分量的功能,这项功能就由内部过程FillAlpha来完成了。

理论上,只要能提供以上3种类型的窗口或设备,都可将其转换为图像数据,但实际并非如此:

一、很多设备是只写的,如打印机设备,并不能反过来提供图像数据(注:我身边没有打印机设备,没做测试,只是根据经验如此认为)。即使是我们认为应该能获取图像的设备上下文句柄,也往往不能达到愿望,如GDI+的TGpBitmap,可以把一个TGpGraphics类型与之关联,按理说,通过TGpGraphics.GetDC获取的设备上下文,应该能获取TGpBitmap的图像数据,可是,我试过多次,得到的只是一个黑色填充的图像;

二、对于显示屏显示的各种窗口,也有可能得不到希望的图像数据,因为在窗口的全部或者部分被遮挡后,Windows系统是不会绘制被挡住的窗口(或者部分窗口),我们只能正确获取设备当前裁剪区域的图像,其余部分不是黑色,就是杂乱无章的,截取它们的图像数据毫无意义。所以,在内部过程GetImageDataFromDC中,获取的只是DC当前裁剪区域(用API GetClipBox计算)的图像数据。

下面是个以上面同名BASM过程等价的纯PAS过程,供学习参考:

procedure FillAlpha(Data: TImageData); var P: PLongWord; I, Count: Integer; begin P := Data.Scan0; Count := Data.Width * Data.Height; for I := 1 to Count do begin P^ := P^ or $ff000000; Inc(P); end; end;

简单应用举例,获取当前窗口的图像数据,并向右移动50后,显示在当前窗口中,也可把Canvas改为Handle:

var Data: TImageData; begin Data := GetImageData(Canvas); DrawImage(Canvas, 50, 0, Data); FreeImageData(Data); end;

代码中的DrawImage过程见《Delphi图像处理 -- 图像显示》。

文章中使用GDI+版本下载地址和说明见《GDI+ for VCL基础 -- GDI+ 与 VCL》。

文章中所用数据类型及一些过程见《Delphi图像处理 -- 数据类型及内部过程》和《Delphi图像处理 -- 图像像素结构与图像数据转换》。

尽管我十分努力,但水平有限,错误在所难免,欢迎指正和指导。邮箱地址:

maozefa@hotmail.com

说明:本文代码于2010.5.20重新修订过。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值