首页 > 代码库 > GDI双缓冲的一些学习

GDI双缓冲的一些学习

  重新回到VC做了几个月了,之前都是些业务代码的多一些,这两天开始要修改一个软件的界面,突然发现改起来挺吃力的,搞个背景图片都研究老半天,改来改去结果发现改错窗口,很是郁闷;看到里面一些代码之后,总有些疑问,所以回来研究了一下。

  双缓冲:主要是为了避免直接绘制在设备上会导致闪烁的问题,针对网上的一些文章,对流程做了一下梳理,流程主要是这样的:

  1、从设备DC创建第一层内存DC;

CDC backDC;backDC.CreateCompatibleDC(pDC);

  2、创建设备兼容的位图;

HBITMAP hbmp = ::CreateCompatibleBitmap(pDC->m_hDC, rect.Width(), rect.Height());

  3、将位图选入第一层DC;

CBitmap* pOldBitmap = backDC.SelectObject(CBitmap::FromHandle(hbmp));

  4、创建第二层内存DC;

CDC srcDC;srcDC.CreateCompatibleDC(pDC);

  5、导入要绘画的图片,将位图选入第二层DC,然后复制到第一层缓冲里

HBITMAP bmpDest = (HBITMAP)::LoadImage(AfxGetInstanceHandle(), strFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if(bmpDest == NULL)
  return FALSE;
CBitmap *pOldBitmapSrc = http://www.mamicode.com/srcDC.SelectObject(CBitmap::FromHandle(bmpDest));
BITMAP bm;
GetObject(bmpDest, sizeof(bm), &bm);backDC.SetStretchBltMode(HALFTONE); //防止失真backDC.StretchBlt(0, 0, rect.Width()/2, rect.Height(), &srcDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);

  6、将第一层位图绘制好的内容,复制到第一层DC中;

pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &backDC, 0, 0, SRCCOPY);

  7、释放相关的资源(DC, HBITMAP等);

 

  对于双缓冲来说,还有额外一种作用,就是将要绘制的图片,存到bmp文件中;对于上面第二步创建的位图hbmp,在最后得到它就是要输出的内容,我们可以针对这个HBITMAP的句柄,来对他进行相关的操作,可以用来绘制到不同的窗口,或者保存到图片,保存图片的代码是根据网上找到的代码进行保存的,如下:

BOOL SaveBitmapToFile(HBITMAP hBitmap, LPCTSTR lpFileName) {     HDC hDC; //设备描述表     int iBits; //当前显示分辨率下每个像素所占字节数     WORD wBitCount; //位图中每个像素所占字节数     DWORD dwPaletteSize=0, //定义调色板大小,   ,              dwBmBitsSize,  //位图中像素字节大小            dwDIBSize,  //位图文件大小            dwWritten;  //写入文件字节数    BITMAP Bitmap; //位图属性结构     BITMAPFILEHEADER bmfHdr; //位图文件头结构     BITMAPINFOHEADER bi; //位图信息头结构     LPBITMAPINFOHEADER lpbi; //指向位图信息头结构     HANDLE fh, hDib, hPal,hOldPal=NULL; //定义文件,分配内存句柄,调色板句柄     //计算位图文件每个像素所占字节数     HDC hWndDC = ::CreateDC("DISPLAY",NULL,NULL,NULL);     hDC = ::CreateCompatibleDC( hWndDC ) ;     iBits = ::GetDeviceCaps(hDC, BITSPIXEL) * ::GetDeviceCaps(hDC, PLANES);     ::DeleteDC(hDC);         if (iBits <= 1)         wBitCount = 1;     else if (iBits <= 4)         wBitCount = 4;     else if (iBits <= 8)         wBitCount = 8;     else if (iBits <= 24)         wBitCount = 24;     else         wBitCount = 24 ;     //计算调色板大小     if (wBitCount <= 8)         dwPaletteSize = (1 << wBitCount) * sizeof(RGBQUAD);     //设置位图信息头结构     GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);     bi.biSize = sizeof(BITMAPINFOHEADER);     bi.biWidth = Bitmap.bmWidth;     bi.biHeight = Bitmap.bmHeight;     bi.biPlanes = 1;     bi.biBitCount = wBitCount;     bi.biCompression = BI_RGB;     bi.biSizeImage = 0;     bi.biXPelsPerMeter = 0;     bi.biYPelsPerMeter = 0;     bi.biClrUsed = 0;     bi.biClrImportant = 0;     dwBmBitsSize = ((Bitmap.bmWidth * wBitCount+31)/32) * 4 * Bitmap.bmHeight ;     //为位图内容分配内存     hDib = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));     lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);     *lpbi = bi;         // 处理调色板     hPal = GetStockObject(DEFAULT_PALETTE);     if (hPal)     {         hDC = ::GetDC(NULL);         hOldPal = ::SelectPalette(hDC, (HPALETTE)hPal, FALSE);         RealizePalette(hDC);     }         // 获取该调色板下新的像素值     GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight,     (LPSTR)lpbi + sizeof(BITMAPINFOHEADER)     +dwPaletteSize,     (LPBITMAPINFO )     lpbi, DIB_RGB_COLORS);     //恢复调色板     if (hOldPal)     {         SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);         RealizePalette(hDC);         ::ReleaseDC(NULL, hDC);     }         //创建位图文件     fh = CreateFile(lpFileName, GENERIC_WRITE,                         0, NULL, CREATE_ALWAYS,                         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);     if (fh == INVALID_HANDLE_VALUE)         return FALSE;     // 设置位图文件头     bmfHdr.bfType = 0x4D42; // "BM"     dwDIBSize = sizeof(BITMAPFILEHEADER)                     + sizeof(BITMAPINFOHEADER)                     + dwPaletteSize + dwBmBitsSize;     bmfHdr.bfSize = dwDIBSize;     bmfHdr.bfReserved1 = 0;     bmfHdr.bfReserved2 = 0;     bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER)             + (DWORD)sizeof(BITMAPINFOHEADER)             + dwPaletteSize;     // 写入位图文件头     WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);     // 写入位图文件其余内容     WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);     //清除     GlobalUnlock(hDib);     GlobalFree(hDib);     CloseHandle(fh);         return TRUE; }

  类似的,我们也可以用双缓冲配合StretchBlt来做一些图片修改尺寸之类的操作;

  做了一些界面编程之后,觉得美工真的很重要,大概是以前做前端界面做得少了吧。

  

 

GDI双缓冲的一些学习