首页 > 代码库 > 【MFC】截图编辑插件技术总结(2):现有截屏API分析

【MFC】截图编辑插件技术总结(2):现有截屏API分析

作为系列博文的第一篇,先写些简单的吧,也是对自己学习过的东西的总结。

BitBlt

首先,是最常用的贴图函数之一的BitBlt,作用是从源DC中拷贝指定大小的图片到目标DC中,至于DC是什么,这里就不再赘述了,请自行百度。下面给出该函数的参数列表:

BOOL BitBlt(HDC hdcDest,// 目标DC的Handleint nXDest, // 目标位置的左上角X坐标int nYDest, // 目标位置的左上角Y坐标int nWidth, // 需要拷贝的图片的宽int nHeight,// 需要拷贝的图片的高HDC hdcSrc, // 源DC的Handleint nXSrc, // 要拷贝图片的左上角X坐标int nYSrc, // 要拷贝图片的左上角Y坐标DWORD dwRop // 操作标志);

 

需要注意的地方是,这里的坐标都是指DC中的坐标,可以认为DC是一个虚拟的显示设备,该设备以显示区域的左上角作为原点,X轴向右递增,Y轴向下递增,以此建立坐标系。下面以截取全屏为例说明函数用法:

m_width=GetSystemMetrics(SM_CXSCREEN);//获取屏幕分辨率m_height=GetSystemMetrics(SM_CYSCREEN);hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL);//为屏幕创建设备描述表hMemDC = CreateCompatibleDC(hScrDC);//创建兼容的内存设备描述表// 创建一个与屏幕设备描述表兼容的位图hBitmap = CreateCompatibleBitmap(hScrDC, m_width,m_height);// 把新位图选到内存设备描述表中hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);// 把屏幕设备描述表拷贝到内存设备描述表中BitBlt(hMemDC, 0, 0, m_width, m_height,hScrDC, 0, 0, SRCCOPY);

 

这样就可以把屏幕截取到内存DC中用于后续操作了。最后说说操作标志这个参数,上面的代码中使用的是SRCCOPY,意思是直接复制,它还有很多选择,详情可以查看MSDN,可能用到的有SRCAND、SRCPAINT等,复制时将原图片与目标图片进行逻辑与,异或,或运算。可以产生一些特别的效果。

StretchBlt

StretchBlt是BitBlt的变形,主要用于图片的缩放。下面给出该函数的参数列表:

BOOL StretchBlt(HDC hdcDest, // 目标DC的Handleint nXOriginDest,// 目标位置的左上角X坐标int nYOriginDest,// 目标位置的左上角Y坐标int nWidthDest, // 图片目标宽度int nHeightDest, // 图片目标长度HDC hdcSrc, // 源DC的Handleint nXOriginSrc, // 要拷贝图片的左上角X坐标int nYOriginSrc, // 要拷贝图片的左上角Y坐标int nWidthSrc, // 要拷贝图片的宽度int nHeightSrc, // 要拷贝图片的高度DWORD dwRop // 操作标志);

 

从参数可以看出StretchBlt比BitBlt多了两个描述宽度和高度的参数,如果目标宽度高度与源宽度高度都相同则该函数的作用与BitBlt一样。通过改变目标宽度和高度,StretchBlt允许用户在拷贝的同时缩放图片。

PrintWindow

接下来要介绍的是PrintWindow函数,该函数的作用是截取整个窗口的内容。有人可能会问,用BitBlt就可以实现截取整个窗口的内容啦,为什么要有这个函数呢?其实,前面介绍的BitBlt函数有一个问题无法解决,那就是窗口遮挡问题,当想要截取的窗口被其他窗口遮挡住时,用BitBlt就无法截取完整的窗口内容了。这个时候就应该使用PrintWindow来截取完整窗口内容。下面给出函数的参数列表:

BOOL PrintWindow(HWND hwnd, // 要拷贝的窗口句柄HDC hdcBlt, //图片保存的目标DCUINT nFlags // 操作标志);

 

下面通过截取全屏来演示该函数的使用方法:

HWND hwnd=::GetDesktopWindow();CRect* rect;::GetWindowRect(hwnd,rect);int iWidth=rect->Width();int iHeight=rect->Height();HDC hScrDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);HDC BufferDC;BufferDC=::CreateCompatibleDC(hScrDC);HBITMAP BufferBmp;BufferBmp=::CreateCompatibleBitmap(hScrDC,iWidth,iHeight);::SelectObject(BufferDC,BufferBmp);::PrintWindow(hwnd,BufferDC,NULL);

 

操作标志只有一个选项,就是PW_CLIENTONLY,意思是只截取窗口的用户区域部分,否则会全部截取。通过与前面函数的对比就会发现,PrintWindow并没有截取其他窗口的内容。下面说说本人遇到过的一个问题,PrintWindow能否截取带滚动条的窗口的完整内容?答案是不可以,为什么呢?不是说可以截取整个窗口的内容么?这涉及PrintWindow函数的具体实现,对于这个问题本人不想再深挖下去了,有兴趣的同学可以研究下,得出结论后麻烦告知一声啊!这里只提出我的一个猜想,根据MSDN上的资料,PrintWindow函数与WM_PAINT消息相似,众所周知,可以通过SendMessage函数向窗口发送WM_PAINT消息使其刷新窗口。那么,我猜PrintWindow函数是让窗口在指定的DC上刷新了,这样就会只复制该窗口的内容而不会被其他窗口干扰。因为只是进行了刷新,所以就无法截取带滚动条的窗口的全部内容了,只能截取可视区域的内容。

那要怎么解决这个问题呢?参考一些流行截图软件的做法,就是边移动滚动条,边截图,然后再把图片都拼接起来。但是,这又要怎么确保图片对齐呢?这里就不再讨论了,再讨论就跑题了,解决方案会在另一篇博文中作详细介绍,有兴趣的可以期待一下~

IViewObject::Draw

最后这个比较复杂,它主要用于截取IE浏览器的内容,但它不仅仅用于网页,微软的很多控件都实现了该方法,所以它也被用来截取控件内容。这里只对其作简要介绍,后面会写一篇关于网页截图的博文,敬请期待~下面给出函数参数列表:

HRESULT Draw(DWORD dwAspect,LONG lindex,void* pvAspect,DVTARGETDEVICE* ptd,HDC hicTargetDev,HDC hdcDraw,//保存图片的DCconst LPRECTL lprcBounds,const LPRECTL lprcWBounds,BOOL (*) (DWORD)pfnContinue,DWORD dwContinue);

 

函数的大致用法:

pViewObject->Draw(DVASPECT_CONTENT,1,NULL,NULL,NULL,hIEMenDC2,&rectl,NULL,NULL,NULL);

 

好的,截图函数的总结就先到这里了,第一次写博文感觉挺累的,以前做惯了伸手党,现在总算有点东西可以分享了。

本人水平有限,文中难免出现错漏,欢迎各位留言指出,但请注意文明用语,总之轻喷吧!