您的位置:首页 > 其它

怎样得到鼠标光标图相关信息(DirectX 截图取得光标 的方法)

2012-12-15 01:00 316 查看
缘由: 由于DirectX 作截图渲染的时候.. 截取整个桌面的图时,它没有得到光标... 对于做桌面的融合等时,在扩展桌面的操作因为看不到

鼠标光标的原因.对操作带来一定的不便.. 所以要想办法把光标也一起截图渲染的时候搞过去...

解决方案: 得到光标的图的相关信息. 在用DirectX 得到截取桌面图后.. 直接改写对应的像素数据.. (一般光标是 32*32,要改写的数据也就是这么多.效率方面一般不受影响的...)

本文示例程序: 可到我的资源下得到: GetCursorIco.rar...




具体步骤:在MFC 中利用对话框,加一个按钮,对应的函数如下即可>>

不足: 本文其实没有达到能截取到动态鼠标光标的目的,虽然这种应用场合比较少.. 我找了很多专业的截图工具,都是没有截取到动态光标,., 也就是比较一个"忙" 的光标,每次截取的时候,都仅截取到,那光标的第一帧图...

这种应用场合:如果要做截图渲染,每秒几十帧,上百帧的.. 那里截得的光标就不会动态了.. 这种没有解决.,如果有谁有更好的方法.可以告知一下..
wen438671344@qq.com

void CGetCursorIconDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码

// for (int i = 0; i < 200; i++)
{
// Sleep(2000);
DrawCursor(100, 100);
}

}

// 功能:画光标
// posX: 光标要画到的x 坐标,posY :光标要画到的y 坐标.
// 附注: 整个函数内容啰嗦.. 为了达到把相应的图输出来观察.加入了不少内容...都是学习的原因..
//        正式场合使用时,可写得精简..
void CGetCursorIconDlg::DrawCursor(const int posX, const int posY)
{
//  参考 http://bbs.csdn.net/topics/300196964 
//Sleep(2000);// 停2 秒,方便点击了按钮后,移到别的地方能截到别处的光标.(不延时,一点就取到当时的光标了,不利于观察)

CURSORINFO ci;
ci.cbSize=sizeof CURSORINFO;
if( !(::GetCursorInfo(&ci) )) return;

//  判断光标是否为显示状态,若不是,则返回
if (ci.flags != CURSOR_SHOWING)
return;

#if 0
// 得到光标图标信息
ICONINFO iconif;
ZeroMemory(&iconif, sizeof(iconif));
::GetIconInfo(ci.hCursor,&iconif);  // 这样直接得到容易出错,见下面分析
#else
HICON hCursorCopy;

// 注意啊.如果不用这一句,把原来的的位图信息拷贝出来的话. 如果下面的信息操作的时间长一点的话,但是
// 光标在移动的时候已经有变化了.这里光标指向的句柄指向都已经改变了.所以再用 GetBitmapBits 得到的数据时..
// 就出错了..
// 所以,要用到光标的位图信息时,最好是先拷贝一次数据出来...
//
//                                                           2012.12.18 by benben
hCursorCopy = ::CopyIcon(ci.hCursor);

// 得到光标图标信息
ICONINFO iconif;
ZeroMemory(&iconif, sizeof(iconif));
::GetIconInfo(hCursorCopy, &iconif);
#endif

//////////////////////////////////////////////////////////////////////////
// 把光标图标的 颜色图 保存下来观察下(注意,这里要判断一下句柄是否为空,因为有些黑白图标则没有这个颜色图,比如 I-Beam 光标)
CImage  image;
if (NULL != iconif.hbmColor)
{
// 注意啊.  对于一些光标为黑白两种颜色的,根本就不需要 颜色位图.所以这时,hbmColor  为空的情况也是有的..所以这里要判断
// 句柄是否为空.

image.Attach(iconif.hbmColor);
image.Save(_T("..\\cursor\\color.bmp"));
image.Detach();
}
// 保存 掩码图
image.Attach(iconif.hbmMask);
image.Save(_T("..\\cursor\\mask.bmp"));
image.Detach();
//
//////////////////////////////////////////////////////////////////////////

// 从磁盘上加载一幅图.看测试的效果,把测试的效果图输出到 文件夹 cursor 目录下
HBITMAP hBitmap = LoadBitmap(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDB_BITMAP1));
BITMAP bitmap_Src ;
GetObject(hBitmap, sizeof(BITMAP), &bitmap_Src);
CImage imgSource;
imgSource.Attach(hBitmap);

BITMAP bitmap_mask;
// 得到光标 掩码图相关的信息, 注意 bitmap_mask. bmBits 的结果为NULL,
// 可以用 GetBitmapBits 得到数据像素位的信息
GetObject(iconif.hbmMask, sizeof(BITMAP), &bitmap_mask);

BITMAP bitmap_color;
GetObject(iconif.hbmColor, sizeof(BITMAP), &bitmap_color);

HDC hDC = NULL ;
HBITMAP hOldBitmap = NULL ;
if (NULL != iconif.hbmColor)
{
#ifdef NEW_VERSION
hDC = ::CreateCompatibleDC( NULL );
if(!hDC)
return;

hOldBitmap = HBITMAP( ::SelectObject( hDC, iconif.hbmColor ) );
#else
int cBytes = bitmap_color.bmWidthBytes * (bitmap_color.bmHeight);
char *pBitColor = new char[cBytes];
GetBitmapBits(iconif.hbmColor, cBytes, pBitColor);
#endif
}

int maskBytes = bitmap_mask.bmWidthBytes * (bitmap_mask.bmHeight);
char *pBitMask = new char[maskBytes];
GetBitmapBits(iconif.hbmMask, maskBytes, pBitMask);
// 这里应该判断一下能否内存申请成功的..     这里只是 demo .我没有这样做了.. 注意一下.这在实际应用中需要改成类似于
//  以下的..
// 其实我觉得这里因为经常调用.. 用类成员变量(或全局变量)<一个保存内存块的指针,另一个保存大小>来保存申请的内存空间..
// 没有必要每次进入时申请,离开时释放... 应该这样:申请一块内存后.不释放 .. 到下次用到时..
// 判断下次要用到的内存有没有大于当前已经申请的,如果是大于当前已经申请的,才释放当前,另申请一块内存..( benben)
// e.g.
//if ( (!m_pBufferMask) || (m_MaskBytes < BMask) ) // 指针为空 或 当前已经申请的内存小于现在即将要用到的内存的大小
//{
// if(m_pBufferMask)  delete m_pBufferMask;

// m_pBufferMask = new char[BMask];
// if(!m_pBufferMask) break;   // 申请内存空间不成功.
// m_MaskBytes = BMask;    // 保存当前申请的内存空间的大小
//}

// 解说:对于光标为黑白图的.得到的iconif.hbmMask 的图 就是 32*64(前半部分与 要贴光标的图作"与"运算.
//       后半部分,则与图作 "异或" 运算..

if (NULL != iconif.hbmColor)
{
for (int row = 0; row < bitmap_color.bmHeight; ++row)
{
for (int col = 0; col < bitmap_color.bmWidth; ++col)
{
COLORREF color = imgSource.GetPixel(col+ posX, row+posY);
DWORD red = GetRValue(color);
DWORD green = GetGValue(color);
DWORD blue = GetBValue(color);

// 与光标掩码图 作 "与" 运算
DWORD ret = getBitValue(bitmap_mask.bmWidth, bitmap_mask.bmHeight, pBitMask, col, row);
// 注意以下三句不要写成 color &= ret;
red  &= ret;
green &= ret;
blue &= ret;

#ifdef NEW_VERSION  // 这个是新的版本..
// 今天和同事讨论到这个彩色图像不一定就是每个像素是 32 来存储..
// 如果是 32位(也就是说用 24 位表示颜色的情况, 没有颜色映射表.所以像我这样做也就足够了..)
// 如果这个光标的颜色图可能是其他种类的(比如用 16 位来表示一个像素的时候.要显示到屏幕上)必然会有
// 一个颜色映射表... 一个方案是可以自己考虑来通过 位图信息的相关数据来做.. 另一个方案即是我参考了
// MFC 源码的 CImage 类(找atlimage.h这个头文件) 的成员函数 CImage::GetPixel(int, int) 的做法..
//

COLORREF clr = ::GetPixel( hDC, col, row);

red  |= GetRValue(clr);
green |= GetGValue(clr);
blue |= GetBValue(clr);

#else        // 原始版本
// 与光标颜色图 作 "或" 运算(注意以下的顺序)
blue |= pBitColor[bitmap_color.bmWidthBytes * row + col * (bitmap_color.bmBitsPixel/8)];
green |= pBitColor[bitmap_color.bmWidthBytes * row + col * (bitmap_color.bmBitsPixel/8) + 1];
red  |= pBitColor[bitmap_color.bmWidthBytes * row + col * (bitmap_color.bmBitsPixel/8) + 2];
#endif

// 把原来像素设置回去
imgSource.SetPixel(col+posX, row+posY, RGB(red, green, blue));
}
}
}
else
{
for (int row = 0; row < bitmap_mask.bmHeight/2; ++row)
{
for (int col = 0; col < bitmap_mask.bmWidth; ++col)
{
COLORREF color = imgSource.GetPixel(col+posX, row+posY); // 这里取得像素数据

DWORD red = GetRValue(color);
DWORD green = GetGValue(color);
DWORD blue = GetBValue(color);

DWORD ret = getBitValue(bitmap_mask.bmWidth, bitmap_mask.bmHeight, pBitMask, col, row);
color &= ret;

ret = getBitValue(bitmap_mask.bmWidth, bitmap_mask.bmHeight,\
pBitMask, col,  bitmap_mask.bmHeight /2 + row); // 取得后半部分图的对应像素位
color ^= ret;

// 把原来像素设置回去
imgSource.SetPixel(col+posX, row+posY, color);
}
}
}

imgSource.Save(_T("..\\cursor\\result.bmp"));
imgSource.Detach();

// 释放资源
if(pBitMask) delete pBitMask;

#ifdef NEW_VERSION
if(hDC) ::DeleteDC( hDC );
if(hOldBitmap) ::SelectObject( hDC, hOldBitmap );
#else
if(pBitColor) delete pBitColor;
#endif

// 检测内存泄漏..
_CrtDumpMemoryLeaks();

//////////////////////////////////////////////////////////////////////////
// ......
//             这下面可能做DirectX 相关的渲染操作..
// ...

}

// 功能: 取得单色图对应像素位的信息
// 返回值; 如果对应该位为 1 ,则返回0xffffffff; 如果对应位为 0, 则返回 0
DWORD CGetCursorIconDlg::getBitValue(int width, int height, void* p, int x, int y)
{
char *pBit = (char*)p;
int num = x/8;
int bits = x % 8;

if(1 == ((pBit[(width/8) * y + num] >> (7 - bits) ) & 1))
{
return 0xffffffff;
}
else
{
return 0; // 注意如果是像DirectX 得,有 阿尔法 值的情况,这里可返回 0Xff000000; 这样就不至于影响到 阿尔法的值了...
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐