GDI双缓冲的一些学习
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 = 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双缓冲的一些学习的主要内容,如果未能解决你的问题,请参考以下文章