屏幕外绘制GDI+
Off-screen drawing GDI+
我有一个问题-我需要绘制两个png文件,一个在另一个上。当我用通常的方法做的时候,会有一个"闪烁"的效果(第一张图片在一小段时间内透支了第二张图片)。我使用GDI+库,我的WM_PAINT处理看起来像这样:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint( hwnd, & ps );
displayImage(firstImage, hwnd);
displayImage(secondImage, hwnd);
EndPaint( hwnd, & ps );
break;
}
displayImage功能:
void displayImage(HBITMAP mBmp, HWND mHwnd)
{
RECT myRect;
BITMAP bm;
HDC screenDC, memDC;
HBITMAP oldBmp;
BLENDFUNCTION bf;
GetObject(mBmp, sizeof(bm), &bm);
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xff;
bf.AlphaFormat = AC_SRC_ALPHA;
screenDC = GetDC(mHwnd);
GetClientRect(mHwnd, &myRect);
if (mBmp == NULL)
FillRect(screenDC, &myRect, WHITE_BRUSH);
else
{
memDC = CreateCompatibleDC(screenDC);
oldBmp = (HBITMAP)SelectObject(memDC, mBmp);
AlphaBlend (screenDC, 0, 0, myRect.right,myRect.bottom, memDC, 0, 0, bm.bmWidth,bm.bmHeight, bf);
SelectObject(memDC, oldBmp);
DeleteDC(memDC);
ReleaseDC(mHwnd, screenDC);
}
}
加载文件到变量:
HBITMAP mLoadImg(WCHAR *szFilename)
{
HBITMAP result=NULL;
Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(szFilename,false);
bitmap->GetHBITMAP(NULL, &result);
delete bitmap;
return result;
}
firstImage = mLoadImg(L"data\img\screen.png");
secondImage = mLoadImg(L"data\img\screen2.png");
我听说我应该做一个屏幕外的绘画。应该是什么样子呢?
您不需要所有这些。你可以直接使用GDI+:
static Gdiplus::Image *firstImage;
static Gdiplus::Image *secondImage;
case WM_CREATE: // or WM_INITDIALOG if it's dialog
{
firstImage = new Gdiplus::Image(L"data\img\screen.png");
secondImage = new Gdiplus::Image(L"data\img\screen2.png");
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
Gdiplus::Graphics gr(hdc);
gr.DrawImage(firstImage, 0, 0);
gr.DrawImage(secondImage, 0, 0);//<== this will draw transparently
EndPaint(hwnd, &ps);
return 0;
}
然而,这个代码仍然绘制2个图像背靠背与可能的闪烁(像你的原始代码)。在WM_PAINT
中使用双缓冲,以便只完成一个BltBlt
。只需更改为:
if (msg == WM_PAINT)
{
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
HGDIOBJ oldbmp = SelectObject(memdc, hbitmap);
FillRect(memdc, &rc, WHITE_BRUSH);
Gdiplus::Graphics gr(memdc);
gr.DrawImage(firstImage, 0, 0);
gr.DrawImage(secondImage, 0, 0);
BitBlt(hdc, 0, 0, rc.right, rc.bottom, memdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldbmp);
DeleteObject(hbitmap);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}
原代码:
void displayImage(HBITMAP mBmp, HWND mHwnd)
{
HDC hdc = GetDC(mHwnd);
...
}
您应该将函数声明更改为void displayImage(HBITMAP mBmp, HWND mHwnd, HDC hdc)
,然后您可以直接从WM_PAINT
传递hdc
首先,更改displayImage以获取调用者的HDC和RECT,而不是HWND。
void displayImage(HBITMAP mBmp, HDC hdc, const RECT &myRect)
{
if (mBmp == NULL)
FillRect(screenDC, &myRect, WHITE_BRUSH);
else
{
BITMAP bm;
GetObject(mBmp, sizeof(bm), &bm);
HDC memDC = CreateCompatibleDC(screenDC);
HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, mBmp);
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xff;
bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(hdc, 0, 0, myRect.right, myRect.bottom, memDC, 0, 0, bm.bmWidth, bm.bmHeight, bf);
SelectObject(memDC, oldBmp);
DeleteDC(memDC);
}
}
然后,在调用者中创建一个兼容的DC和位图。这些是你做合成的屏幕外空间。使用这个新DC调用displayImage。这将组成屏幕外的png。最后,将合成结果一次性blit到实际的窗口DC。
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT myRect;
GetClientRect(hwnd, &myRect);
// Create an off-screen DC for composing the images.
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmpMem = CreateCompatibleBitmap(hdc, myRect.right, myRect.bottom);
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpMem);
// Compose the images to the offscreen bitmap.
displayImage(firstImage, hdcMem, myRect);
displayImage(secondImage, hdcMem, myRect);
// Blit the resulting composition to the window DC.
BitBlt(hdc, 0, 0, myRect.right, myRect.bottom,
hdcMem, 0, 0, SRCCOPY);
// Clean up the offscreen stuff.
SelectObject(hdcMem, hbmpOld);
DeleteObject(hbmpMem);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
break;
}
最后,如果你仍然看到背景颜色的闪光,看看Pavan Chandaka的答案。
自行处理"WM_ERASEBKGND"消息
实际上在加载第二张图片之前发生了两件事。
- WM_ERASEBKGND首先被触发以填充图像区域,无论当前窗口的背景颜色是什么。
- WM_PAINT渲染动作
文档说,为了避免blink/Flickr,为"WM_ERASEBKGND"提供一个默认处理程序。
下面是链接,选择"不闪烁的控件"。你也有一个例子。
https://msdn.microsoft.com/en-us/library/ms969905.aspx相关文章:
- GDI 绘制到外部窗口 (C++)
- 使用 Win32 将 GDI 绘制大小缩放为窗口大小
- 使用GDI(C )的轮廓绘制文字
- 使用GDI 从ADB Framebuffer绘制数据
- DirectWrite GDI 互操作:使用"IDWriteFontFace"绘制文本的简单方法
- 当我使用 DirectWrite 在 GDI hdc 上绘制文本时,如何设置透明背景
- 如何在MFC对话框上绘制/创建GDI
- 如何使用 GDI API 在 Windows 桌面上绘制
- 打开不同的图像文件,并使用GDI 库在主应用程序窗口的背景上绘制它们
- 在本机 Win32 GDI 中绘制略微透明的蓝色矩形
- 为什么当我绘制大量 (~10k) 简单基元时 GDI 变得无响应
- 绘制和创建内存缓冲区 (Windows GDI)
- GDI+:绘制位图时出现未处理的异常
- 使用类似于Inspect的GDI+(或GDI)在屏幕上绘制
- GDI+只在内存DC上绘制单色
- 如何在 GDI 绘制的 MFC 的 CScrollView 中实现缩放
- 屏幕外绘制GDI+
- GDI+图形对象-当用GDI直接绘制到DC时,抗混叠丢失
- 绘制到GDI+位图
- GDI+ 绘制倒置的字符串