zoukankan      html  css  js  c++  java
  • bmp2gray

    用MFC很容易把当前屏幕截取,并显示在自己程序的UI上。以对话框为例,在执行绘制的单元(比如OnPaint)中调用下面这个函数就能做到:

     
    BOOL CSrnShotDlg::GetMyScreen(
                       CDC *pdc                                      // 目标DC
    )
    {
                       CDC dc;
                       dc.CreateDC("DISPLAY", NULL, NULL, NULL); // 屏幕DC
     
                       CRect clientRect;
                       GetClientRect(clientRect);                                           // 对话框矩形区域
     
                       pdc->BitBlt(0, 0,                                                             // 起始位置
                                         clientRect.Width(),clientRect.Height(),    // 宽高
                                         &dc,                                                                 // CDC对象
                                         0, 0,                                                                    // 源位置
                                         SRCCOPY                                                       // 复制方法
                                         );
                       dc.DeleteDC();
                       return TRUE;
    }
     
    接下来改造一下,把屏幕截图先转换为灰度(Gray Scale)图,再显示出来。转换灰度图的公式是,对一个RGB值,R、G、B分别是其3色分量,计算:
                  Gray = R * 0.299 + G *0.587 + B * 0.114
    然后将Gray分别替换掉原来的3色分量。到这个地方,很自然想到用SetPixel/GetPixel来实现。因为要对DC进行操作,当然就不能直接在上面GetMyScreen里边的dc直接操作了,为此对GetMyScreen进行一下改造,并且,为了程序的可读性,增加一个ConvertToGray函数负责转换(与上面代码不同的地方用红色区分):
     
    void ConvertToGray (CDC * pdc)
    {
                       for (int xx = 0; xx < clientRect.right ; xx ++)
                                         for (int yy = 0; yy < clientRect.bottom ; yy ++)
                                         {
                                                            COLORREF crTemp = pdc->GetPixel(xx,yy);
                                                            BYTE pixelR = GetRValue(crTemp);
                                                            BYTE pixelG = GetGValue(crTemp);
                                                            BYTE pixelB = GetBValue(crTemp);
                                                            BYTE gray = (BYTE) (pixelR * 0.299 + pixelG * 0.587 +pixelB * 0.114);
                                                            pdc->SetPixelV(xx,yy,RGB(gray, gray, gray));
                                         }
    }
     
    BOOL CSrnShotDlg::GetMyScreen(
                       CDC *pdc                                      // 目标DC
    )
    {
                       CDC dc;
                       dc.CreateDC("DISPLAY", NULL, NULL, NULL); // 屏幕DC
     
                       CRect clientRect;
                       GetClientRect(clientRect);                                           // 对话框矩形区域
     
                       CDC          *pMemDC = NULL;                                    // 兼容DC
     
                       pMemDC = new CDC;
                       if (!pMemDC)
                                         return FALSE;
                       pMemDC->CreateCompatibleDC(&dc);
                       ShowWindow(SW_HIDE);
                       pMemDC->BitBlt(0, 0,
                                         clientRect.Width(), clientRect.Height(),
                                         &dc, 0, 0, SRCCOPY);
     
                       ConvertToGray(pMemDC);
     
                       pdc->BitBlt(0, 0,                                                             // 起始位置
                                         clientRect.Width(),clientRect.Height(),    // 宽高
                                         pMemDC,                                                        // CDC对象
                                         0, 0,                                                                    // 源位置
                                         SRCCOPY                                                       // 复制方法
                                         );
                       pMemDC->DeleteDC();
                       delete pMemDC;
                       dc.DeleteDC();
                       return TRUE;
    }
     
    效果出来了,但是并不完美。实际上我用SetPixelV代替了SetPixel,但显示的速度还是很慢,CPU使用率也很高。如何提高效率呢?直接改DC上附着的位图数据似乎是个好办法。下面就转而对CBitmap类对象进行操作。
    因为是直接截屏,所以需要先用CDC::GetDeviceCaps带BITSPIXEL参数获得屏幕色深,因为不同色深的位图的储存方式不同。简要说明一下:16位色位图,每个象素占2字节;24位色,每个象素占3字节;32位色,每个象素占4字节储存空间。我们可以用CBitmap::GetBitmapBits函数来获得位图数据,这其实是一个BYTE数组。这个数组的结构,最简单的是24位色的情况。前面说过了每个象素占3个字节,按数组下标从低到高分别是B、G、R这3色分量,而32位色的情况跟24位色类似,4个字节只不过多了一个alpha值。下面就是处理24位色深的ConvertToGray24。
     
    #define BITS24        (int)(1024 * 768 * 3)
    void ConvertToGray24(CBitmap *pBmp)
    {
                       LPBYTE lpbits = NULL;
                       lpbits = new BYTE[BITS24];
                       if (!lpbits)
                                         return;
     
                       ZeroMemory(lpbits, BITS24);
                       pBmp->GetBitmapBits(BITS24, lpbits);
                       for (int index = 0, j = 0, k = 0; index < BITS24; index ++)
                       {
                                         lpbits[index] = (BYTE)(0.114 * lpbits[index]);
                                         j = index + 1; k = index + 2;
                                         lpbits[j] = (BYTE)(0.587 * lpbits[j]);
                                         lpbits[k] = (BYTE)(0.299 * lpbits[k]);
                                         lpbits[index] += lpbits[j] + lpbits[k];
                                         lpbits[j] = lpbits[index];
                                         lpbits[k] = lpbits[index];
                                         index = k;
                       }
     
                       pBmp->SetBitmapBits(BITS24, lpbits);
                       delete [] lpbits;
    }
     
    当GetDeviceCaps(BITSPIXEL)返回16的时候,又有两种情况:16位色和15位色。16位色的情况下,位图数组使用2字节保存数据,其中从高位往低位分别是B、G、R这3色分量按位5:6:5占用。需要用位操作来获得每个分量的色值:
     
    #define GetRValueX(rgb)      ((BYTE)(rgb) & 0x1f)
    #define GetGValueX(rgb)      ((BYTE)(((rgb) & 0x07E0) >> 5))
    #define GetBValueX(rgb)      ((BYTE)(((rgb) & 0xF800) >> 11))
    #define RGBX(r,g,b) \
              ((WORD)(((BYTE)(r)|((WORD)((BYTE)(g))<<5))|(((WORD)(BYTE)(b))<<11)))
     
    要注意的是因为绿色分量占用了6bit,其储存精度是其它两个分量的2倍,所以在进行后继的计算的时候公式的因数会有所改变。(另外,使用15位色的适配器比较少,其储存规则也是占用2字节,但是最高位无意义,其余15位按5:5:5分配,这里不详细讨论了。)
    #define BITS16        (int)(1024 * 768 * 2)
    void ConvertToGray16(CBitmap *pBmp)
    {
                       LPBYTE lpbits = NULL;
                       WORD *wBits;
                       lpbits = new BYTE[BITS16];
                       if (!lpbits)
                                         return;
     
                       ZeroMemory(lpDibits, BITS16);
                       pBmp->GetBitmapBits(BITS16, lpbits);
                       for (int index = 0, j = 0, k = 0; index < BITS16; index ++)
                       {
                                         wBits = (WORD *)(lpbits + index);
                                         BYTE pixelR = GetRValueX(*wBits) * 2;
                                         BYTE pixelG = GetGValueX(*wBits) ;     // 注意系数
                                         BYTE pixelB = GetBValueX(*wBits) * 2;
                                         BYTE gray =(BYTE) (pixelR * 0.299 + pixelG * 0.587 +pixelB * 0.114);
                                         *wBits = RGBX(gray / (BYTE)2, gray, gray / (BYTE)2);
                                         index ++;
                       }
                       pBmp->SetBitmapBits(BITS16, lpbits);
                       delete [] lpbits;
    }
     
     
    最后,第三次改造GetMyScreen:
     
    BOOL CSrnShotDlg::GetMyScreen(
                       CDC *pdc                                      // 目标DC
    )
    {
                       CDC dc;
                       dc.CreateDC("DISPLAY", NULL, NULL, NULL); // 屏幕DC
     
                       CRect clientRect;
                       GetClientRect(clientRect);                                           // 对话框矩形区域
     
                       CDC          *pMemDC = NULL;                                    // 兼容DC
                       CBitmap *pBmp = NULL;                                           // 兼容位图
     
                       pMemDC = new CDC;
                       if (!pMemDC)
                                         return FALSE;
                       pMemDC->CreateCompatibleDC(&dc);
                      
                       pBmp = new CBitmap;
                       if (!pBmp)
                       {
                                         pMemDC->DeleteDC();
                                         delete pMemDC;
                                         return FALSE;
                       }
                       pBmp->CreateCompatibleBitmap(&dc, clientRect.Width(),clientRect.Height());
                       pMemDC->SelectObject(pBmp);
                       ShowWindow(SW_HIDE);
                       pMemDC->BitBlt(0, 0,
                                         clientRect.Width(), clientRect.Height(),
                                         &dc, 0, 0, SRCCOPY);
     
                       switch(pMemDC->GetDeviceCaps(BITSPIXEL))
                       {
                       case: 16
                                         ConvertToGray16(pBmp);
                                         break;
                       case: 24
                                         ConvertToGray24(pBmp);
                                         break;
                       case: 32
                                         ConvertToGray32(pBmp);                          //未给出
                                         break;
                       default:
                                         pBmp->DeleteObject();
                                         pMemDC->DeleteDC();
                                         delete pBmp;
                                         delete pMemDC;
                                         dc.DeleteDC();
                                         return FALSE;
                       }
                       pdc->BitBlt(0, 0,                                                             // 起始位置
                                         clientRect.Width(),clientRect.Height(),    // 宽高
                                         pMemDC,                                                       // CDC对象
                                         0, 0,                                                                    // 源位置
                                         SRCCOPY                                                       // 复制方法
                                         );
     
                       pBmp->DeleteObject();
                       pMemDC->DeleteDC();
                       delete pBmp;
                       delete pMemDC;
                       dc.DeleteDC();
                       return TRUE;
    }

    ----------------------------------------------------

    附文件读取

    ConvertBmp24ToBmp256
    {
     FILE *fp = fopen("test.bmp","rb");
     if(!fp)return;

     BITMAPFILEHEADER hdr;
     
     fread(&hdr,1,sizeof(hdr),fp);

     if(!(((hdr.bfType & 0xff) == 'B') &&  ((hdr.bfType >> 8) == 'M')))
     {
      fclose(fp);
      return;
     }

     BITMAPINFOHEADER bih;

     fread(&bih,1,sizeof(bih),fp);

     if(bih.biBitCount != 24 || bih.biCompression != 0)
     {
      fclose(fp);
      return;
     }

     unsigned char *pBuf = new unsigned char[bih.biSizeImage];

     fread(pBuf,bih.biSizeImage,sizeof(unsigned char),fp);


     FILE *out = fopen("testout.bmp","wb");
     if(!out)
     {
      delete []pBuf;
      fclose(fp);
      return;
     }

     unsigned char *pOutBuf = new unsigned char[bih.biWidth * bih.biHeight];
     unsigned char *tmp = pBuf;
     unsigned char *tmp2 = pOutBuf;
     for(unsigned int i = 0; i < bih.biWidth * bih.biHeight;i++)
     {
      tmp2[i] = (unsigned char)((*tmp) * .114 + *(tmp + 1) * .587 + *(tmp + 2) * .299);
      tmp += 3;
     }

     RGBQUAD q[256];
     for( i = 0; i < 256; i++)
     {
      q[i].rgbRed = q[i].rgbGreen = q[i].rgbBlue = i;
      q[i].rgbReserved = 0;
     }

     bih.biBitCount = 8;
     bih.biSizeImage = bih.biWidth * bih.biHeight;

     hdr.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
      + sizeof(q) + bih.biSizeImage;
     hdr.bfOffBits += sizeof(q);

     fwrite(&hdr,1,sizeof(hdr),out);
     fwrite(&bih,1,sizeof(bih),out);
     fwrite(q,256,sizeof(RGBQUAD),out);
     fwrite(pOutBuf,bih.biSizeImage,sizeof(unsigned char),out);

     delete []pOutBuf;
     fclose(out);
     delete []pBuf;
     fclose(fp);

  • 相关阅读:
    打开安装 好的Microsoft Dynamics CRM 4.0 报错误为 Caller does not have enough privilege to set CallerOriginToken to the specified value 的解决办法
    基于 Windows Server 2008 的计算机对 Microsoft Dynamics CRM 4.0 的支持
    Microsoft Dynamics CRM 4.0 如何添加自定义按钮
    Microsoft Dynamics CRM 4.0 Plugin 取值,赋值,查询
    C# 中的 enum(枚举) 类型使用例子
    vue事件的绑定
    表单验证2
    node中模块
    node模块的引入
    node中的读文件
  • 原文地址:https://www.cnblogs.com/swordzj/p/2034771.html
Copyright © 2011-2022 走看看