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

来源:互联网 发布:象棋路边残局知乎 编辑:程序博客网 时间:2024/05/23 15:44

缘由: 由于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);  // 这样直接得到容易出错,见下面分析#elseHICON hCursorCopy;// 注意啊.如果不用这一句,把原来的的位图信息拷贝出来的话. 如果下面的信息操作的时间长一点的话,但是// 光标在移动的时候已经有变化了.这里光标指向的句柄指向都已经改变了.所以再用 GetBitmapBits 得到的数据时..// 就出错了.. // 所以,要用到光标的位图信息时,最好是先拷贝一次数据出来...// //                                                           2012.12.18 by benbenhCursorCopy = ::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_VERSIONif(hDC) ::DeleteDC( hDC );if(hOldBitmap) ::SelectObject( hDC, hOldBitmap );#elseif(pBitColor) delete pBitColor;#endif// 检测内存泄漏.._CrtDumpMemoryLeaks();//////////////////////////////////////////////////////////////////////////// ......//             这下面可能做DirectX 相关的渲染操作..// ...}// 功能: 取得单色图对应像素位的信息// 返回值; 如果对应该位为 1 ,则返回0xffffffff; 如果对应位为 0, 则返回 0DWORD 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; 这样就不至于影响到 阿尔法的值了...}} 


 

 

 


 

 

原创粉丝点击