如何从DC中取出Bitmap以完成图象抖动

这两天一直被如何从一个图象抖动的问题所困扰, 想了两天, 调试了两天终于搞明白了.

      问题是这样的,我已知图象每个点的数据, 并且已经有在dc上绘制成图象的代码, 开始我的想法是从DC上得到Bitmap以完成图象抖动. 后来发现这种思路不对. 因为当前只有1个打印机dc, 我无法创建一个24位的dc从而创建内存位图, 选到内存dc中, 然后在内存dc中绘图. 可能你会说为什么不用打印机dc来创建一个内存dc来, 再创建一个24位的位图然后选到内存dc中呢, 事实证明dc与Bitmap不兼容, 是选不进去, 包括颜色位数不同, 区域大小不对.   所以同理用CreateDC创建一个显示dc也是行不通的. 最终的解决办法是创建一个打印机关联的内存dc以及一个内存bitmap, 内存bitmap选入内存dc, 然后根据原来的代码生成24位位图的图象数据, 接着创建一个24位位图,将生成好的图象数据设置进去. 这个位图不用选进dc,当然想选也选不进去. 这时就可以将这个位图进行一下抖动, 将这个24位抖动过的位图转换成与打印机关联的那个内存位图就可以了. 当然别忘了将内存dc拷贝到打印机上噢.

      下面说说曾经困扰我多时的几点. 首先我们熟悉的是CreateCompatibleBitmap, 来创建一个内存位图. 但是我们指定不了位图的颜色位数, 这时需要就需要用CreateBitmap了. 第一第二个参数是位图的宽度及高度, 一定注意这两个参数是打印机屏幕上实实在在的点数, 也就是说传过来逻辑坐标的话, 需要用LPtoDP来转换成设备坐标.而调用FillSolidRect以及绘图的时候用到的是逻辑坐标, 也就是说你的内存dc的映射模式一定要设置对. 另外如果你的绘制区域不是从原点开始的话, 记得用在内存dc中调用SetWindowOrg. 遇到另外的一个问题是CreateBitmap会失败. 一种情况是传进来的逻辑坐标top在bottom的下面, 意思说要想创建成功, rect的top要在bottom的上面才行. 比如top 0 bottom -100, top -100 bottom -200. 另一种失败的情况是位图过大, 这个可用下面的解决办法

     if (m_bitmap.m_hObject == NULL)
    {
     m_pFile = new CFile(szVmfile, CFile::modeCreate|CFile::modeReadWrite);
     m_pbi = new BITMAPINFOHEADER;
     m_pbi->biSize    = sizeof (BITMAPINFOHEADER);
     m_pbi->biWidth    = m_rect.Width(); // 注意m_rect要转换成设备坐标
     m_pbi->biHeight    = m_rect.Height();
     m_pbi->biPlanes    = 1;
     m_pbi->biBitCount   = nBitcount;   // 颜色位数
     m_pbi->biCompression = BI_RGB;
     m_pbi->biSizeImage   = 0;
     m_pbi->biXPelsPerMeter = 0;
     m_pbi->biYPelsPerMeter = 0;
     m_pbi->biClrUsed   = 0;
     m_pbi->biClrImportant = 0;

     m_pbi->biSizeImage = ((((m_pbi->biWidth * m_pbi->biBitCount) + 31) & ~31) >> 3) * m_pbi->biHeight;
     m_hFile = ::CreateFileMapping((HANDLE)m_pFile->m_hFile,
      NULL, PAGE_READWRITE, 0, m_pbi->biSizeImage, NULL);
    
     if (m_hFile == NULL)
     {
      TRACE("Error : Failed to create file mapping object!/n");
     }

     HBITMAP hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(),
      (LPBITMAPINFO)m_pbi, DIB_RGB_COLORS, &m_pBits, m_hFile, 0);
    
     TRACE("Save BITMAP size %ud, height %d/n", m_pbi->biSizeImage, m_pbi->biHeight);
    
     if (hBitmap == NULL)
     {
      TRACE("Error : Failed to create bitmap object!/n");
      CloseHandle(m_hFile);
      m_hFile = NULL;
      return;
     }
    
     m_bitmap.Attach(hBitmap);
    }

   还有一个问题是CBitmap中的函数GetBitmapBits, SetBitmapBits的数据缓冲区的大小如何确定, 是这样的

   BITMAP bmpInfo;
   bitmap.GetBitmap(&bmpInfo);
    int nByteCount = bmpInfo.bmHeight * bmpInfo.bmWidthBytes;

   将24位位图转换成黑白位图时, 两个位图的bmpInfo.bmBitsPixel分别是24和1. bmpInfo.bmWidthBytes却并不是正比关系. bmpInfo.bmWidthBytes意思是说我图像的一行所占的字节数. 开始的时候老缓冲区越界, 然后我把缓冲区人为的搞大一些,结果呢GetBitmapBits就失败了, 看来GetBitmapBits必须要缓冲区设对才能成功啊. 后来发现循环转换的部分有问题, 重新根据打印机的位图的 bmHeight 和 bmWidthBytes来循环, 结果图象出来是乱的. 想了很久, 试了很久, 调试了很久, 终于找到原因了. 我用的黑白打印机的一个点是用一位来表示的, 但是如果是图象的一行结束并没有用过整一个字节, 或是一个字, 他会补上的. 也就是说用打印机位图的 bmHeight 和 bmWidthBytes来循环会导致从24位位图数据缓冲区取数据的时候, 偏移量算错. 后来我改成用24位位图的 bmHeight 和 bmWidthBytes来循环, 问题终于解决了, 呵呵, 贴下24位位图转成黑白位图的代码.

CDC ptdc; // 打印机DC
   CBitmap bitmap;
    ptdc.CreateCompatibleDC(m_pDC); // 创建打印机兼容的内存dc, m_pDC是打印机dc
    ptdc.SetMapMode(m_pDC->GetMapMode());
bitmap.CreateCompatibleBitmap(m_pDC, m_drect.Width(), m_drect.Height()); // m_drect是设备坐标
   ptdc.SelectObject(&bitmap);
   ptdc.SetWindowOrg(CPoint(m_rect.left, m_rect.top));
ptdc.FillSolidRect(m_rect, ptdc.GetBkColor());
BITMAP bmpInfo;
   bitmap.GetBitmap(&bmpInfo);
   int nPrintByteCount = bmpInfo.bmHeight * bmpInfo.bmWidthBytes;
   int nPrintColorBit = bmpInfo.bmBitsPixel;
int nPrintHeight = bmpInfo.bmHeight;
int nPrintWidth = bmpInfo.bmWidthBytes;
   BYTE* pPrintData = new BYTE[nPrintByteCount]; // 打印机兼容位图的数据缓冲区
    ZeroMemory(pPrintData, nPrintByteCount);

    m_bitmap.GetBitmap(&bmpInfo); // m_bitmap是用CreateBitmap生成好的24位位图
   int nMemColorBit = bmpInfo.bmBitsPixel;
   int nMemHeight = bmpInfo.bmHeight;
   int nMemWidthBytes = bmpInfo.bmWidthBytes;
    int nByteCount = bmpInfo.bmHeight * bmpInfo.bmWidthBytes;
   BYTE *pData = new BYTE[nByteCount];   // 24位图的数据缓冲区

   m_bitmap.GetBitmapBits (nByteCount , pData);

   for (int i = 0; i < nMemHeight; i++)
     {
      int nPrintPos = i * nPrintWidth; // 一行的起始
      for (int j = 0; j < nMemWidthBytes / 3; j++) // 图像以像素点为单位的列序号
      {
       int nPos = i * nMemWidthBytes + 3 * j;
       int nColor = pData[nPos];
       int p = j / 8;
       int k = j % 8;
       int b = 1;
       if (nColor > 127)
          pPrintData[nPrintPos + p] |= (b << (7 - k));
      }
     }

    bitmap.SetBitmapBits(nPrintByteCount, pPrintData);
    m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(), &ptdc, m_rect.left,   
               m_rect.top,   SRCCOPY);
    delete pPrintData;
    delete pData;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值