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

缘由: 由于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; 这样就不至于影响到 阿尔法的值了...
}
}


 


 

 

 


 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值