GdiplusFlat(7)双缓冲绘图和DC透明复制

本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处: http://blog.csdn.net/zuishikonghuan/article/details/47316261

在上几篇中,我们说到了通过GdipCreateFromHDC函数将HDC(设备上下文句柄)装换成GDI+的Graphics对象,讲到了画笔和画刷的使用,并演示了何利用GDI+Flat来画图片和画文字。

其实,我们使用GdiplusFlat的根本目的并不是为了作图,而是为了做出一个漂亮的界面,这年头,用户喜欢个性的、美观的界面,而不像原来一样,只要功能实用,界面用系统的窗口主题无所谓。这也恰恰证明了计算机的普及,而不是专业人士的专用品了。

那么,我们在自绘界面的时候往往遇到过这样的问题:

1。窗口在自绘时闪烁问题严重。

2。自绘不能一次完成,用户看到的现象是,窗口先画出来背景,然后是标题,然后图片文字出现。这样的程序想必任何人都不愿意用。

解决之道:使用“双缓冲绘图

何谓“双缓冲绘图”?

首先我们需要明白,我们直接用GdiplusFlat或者其他东西绘图时,是直接绘制到显示设备上的,如左图:

GdiplusFlat(7)双缓冲绘图和DC透明复制_第1张图片      GdiplusFlat(7)双缓冲绘图和DC透明复制_第2张图片

也就是说,我们在图形设备上”一笔笔作图“时,因为每一笔之间有间隔,所以用户就看到自绘不能一次完成

那么闪烁的原因呢?闪烁是因为,每一次绘图,都要先把之前的图形抹去,再绘新图,所以造成了闪烁问题。

双缓冲绘图(如右图所示),是指创建一个和窗口的DC大小一样的”内存DC“,先把需要画的东西一步步画在内存DC里,然后一次性复制到窗口DC上,由于复制是一次性的,因此不需要先抹除原先的的内容,因此就没有闪烁问题了,由于复制是一次性的,用户看起来窗口就好像一次性绘制好了。

至于说内存DC是何时创建,这个就根据需要了,我一般是先创建,不销毁,用的时候用,如果什么时候需要绘图什么时候申请会影响效率

创建内存DC一般采用CreateCompatibleDC函数

此函数的原型:

HDC CreateCompatibleDC(
  _In_ HDC hdc
);

参数hdc:现有的 DC 的句柄。如果此句柄为 NULL,则该函数将创建与应用程序的当前屏幕兼容内存 DC。

返回值:成功返回内存DC的句柄,失败返回NULL

创建完之后需要创建设备无关位图对象,用CreateDIBSection函数

HBITMAP CreateDIBSection(
  _In_        HDC        hdc,
  _In_  const BITMAPINFO *pbmi,
  _In_        UINT       iUsage,
  _Out_       VOID       **ppvBits,
  _In_        HANDLE     hSection,
  _In_        DWORD      dwOffset
);

hdc:设备上下文句柄,此处为0即可

pbmi:一个BITMAPINFO结构的指针

后4个参数为0即可

BITMAPINFO结构:

typedef struct tagBITMAPINFO {
  BITMAPINFOHEADER bmiHeader;
  RGBQUAD          bmiColors[1];
} BITMAPINFO, *PBITMAPINFO;

bmiColors:留空即可

bmiHeader:BITMAPINFOHEADER结构

typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;


biSize:BITMAPINFOHEADER结构大小

biWidth、biHeight:宽度、高度

biPlanes:必须为1

biBitCount:一个像素点的颜色位数,一般为32

biCompression--最后:留空即可

源码:

HDC mdc;

	case WM_CREATE:
		mdc = CreateCompatibleDC(0);
		BITMAPINFO bitmapinfo;
		RtlZeroMemory(&bitmapinfo, sizeof(BITMAPINFO));

		bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bitmapinfo.bmiHeader.biWidth = 500;
		bitmapinfo.bmiHeader.biHeight = 500;
		bitmapinfo.bmiHeader.biPlanes = 1;
		bitmapinfo.bmiHeader.biBitCount = 32;
		
		HBITMAP b,old;
		b = CreateDIBSection(0, &bitmapinfo, 0, 0, 0, 0);
		old = (HBITMAP)SelectObject(mdc, b);
		if (old != NULL){
			DeleteObject(old);
		}
		DeleteObject(b);
		break;
	case WM_DESTROY://窗口已经销毁
		DeleteDC(mdc);
		PostQuitMessage(0);//退出消息循环,结束应用程序
		return 0;
		break;
随后mdc就可以直接用了

下面说说DC复制:

DC复制是指把一个DC中的指定部分(或全部)复制到另一个DC中,上面说的DC既可以说窗口DC,也可以是内存DC

StretchBlt函数:

BOOL StretchBlt(
  _In_ HDC   hdcDest,
  _In_ int   nXOriginDest,
  _In_ int   nYOriginDest,
  _In_ int   nWidthDest,
  _In_ int   nHeightDest,
  _In_ HDC   hdcSrc,
  _In_ int   nXOriginSrc,
  _In_ int   nYOriginSrc,
  _In_ int   nWidthSrc,
  _In_ int   nHeightSrc,
  _In_ DWORD dwRop
);


hdcDest:要复制到的目标DC句柄

nXOriginDest、nYOriginDest:要复制到目标DC的左上角坐标

nWidthDest、nHeightDest:要复制到目标DC上的图形的宽度,高度

hdcSrc:被复制的DC句柄

nXOriginSrc、nYOriginSrc:被复制的左上角

nWidthSrc、nHeightSrc:被复制的宽度、高度

dwRop:光栅运算操作,一般为 SRCCOPY(直接复制)

返回值:非0成功,0失败

透明复制:

AlphaBlend函数:

BOOL AlphaBlend(
  _In_ HDC           hdcDest,
  _In_ int           xoriginDest,
  _In_ int           yoriginDest,
  _In_ int           wDest,
  _In_ int           hDest,
  _In_ HDC           hdcSrc,
  _In_ int           xoriginSrc,
  _In_ int           yoriginSrc,
  _In_ int           wSrc,
  _In_ int           hSrc,
  _In_ BLENDFUNCTION ftn
);


hdcDest:要复制到的目标DC句柄

xoriginDest、yoriginDest:要复制到目标DC的左上角坐标

wDest、hDest:要复制到目标DC上的图形的宽度,高度

hdcSrc:被复制的DC句柄

xoriginSrc、yoriginSrc:被复制的左上角

wSrc、hSrc:被复制的宽度、高度

ftn:一个BLENDFUNCTION结构

typedef struct _BLENDFUNCTION {
  BYTE BlendOp;
  BYTE BlendFlags;
  BYTE SourceConstantAlpha;
  BYTE AlphaFormat;
} BLENDFUNCTION, *PBLENDFUNCTION, *LPBLENDFUNCTION;


BlendOp:只能是:AC_SRC_OVER

BlendFlags:必须是0

SourceConstantAlpha:透明度

AlphaFormat:只能为AC_SRC_ALPHA

完整源码:

#include "stdafx.h"
#include <gdiplus.h>
#include <gdiplusflat.h>
#pragma comment(lib,"gdiplus.lib")//very important

#include <windows.h>
#include <windowsx.h>
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"gdi32.lib")

#pragma comment(lib,"Msimg32.lib")

//GDI+Flat
typedef struct _GdiplusStartupInput{
	unsigned int GdiplusVersion;
	unsigned int DebugEventCallback;
	BOOL SuppressBackgroundThread;
	BOOL SuppressExternalCodecs;
}GdiplusStartupInput;

extern "C" int WINAPI GdiplusStartup(int* token, GdiplusStartupInput *input, int *output);
extern "C" void WINAPI GdiplusShutdown(int token);
extern "C" int WINAPI GdipCreateFromHDC(HDC hdc, int* graphics);
extern "C" int WINAPI GdipDeleteGraphics(int graphics);
//画笔
extern "C" int WINAPI GdipCreatePen1(unsigned int argb_color, float width, int unit, int** pen);
extern "C" int WINAPI GdipDeletePen(int* pen);
//画矩形 画直线
extern "C" int WINAPI GdipDrawRectangle(int graphics, int* pen, float x, float y, float width, float height);
extern "C" int WINAPI GdipDrawLine(int graphics, int* pen, float x1, float y1, float x2, float y2);
//画刷
typedef struct _PointF{
	float x;
	float y;
}PointF;
extern "C" int WINAPI GdipCreateSolidFill(unsigned int argb_color, int** brush);
extern "C" int WINAPI GdipCreateLineBrush(PointF* point1, PointF* point2, unsigned int argb_color1, unsigned int argb_color2, int wrapMode, int** lineGradient);
extern "C" int WINAPI GdipDeleteBrush(int* brush);
//画填充矩形
extern "C" int WINAPI GdipFillRectangle(int graphics, int* brush, float x, float y, float width, float height);
//画图片
extern "C" int WINAPI GdipLoadImageFromFile(WCHAR* filename, int** image);
extern "C" int WINAPI GdipLoadImageFromStream(LPSTREAM stream, int** image);
extern "C" int WINAPI GdipGetImageDimension(int* image, float* width, float* height);
extern "C" int WINAPI GdipDrawImageRect(int graphics, int* image, float x, float y, float width, float height);
extern "C" int WINAPI GdipDisposeImage(int* image);
//画文字
typedef struct _RectF{
	float x;
	float y;
	float Width;
	float Height;
}RectF;
extern "C" int WINAPI GdipSetTextRenderingHint(int graphics, int mode);
extern "C" int WINAPI GdipSetSmoothingMode(int graphics, int smoothingMode);
extern "C" int WINAPI GdipCreateFontFamilyFromName(WCHAR* name, int* fontCollection, int** fontFamily);
extern "C" int WINAPI GdipCreateStringFormat(int formatAttributes, short language, int** format);
extern "C" int WINAPI GdipSetStringFormatAlign(int* format, int align);
extern "C" int WINAPI GdipCreateFont(int* fontFamily, float emSize, int style, int unit, int** font);
extern "C" int WINAPI GdipDrawString(int graphics, WCHAR* string, int length, int* font, RectF* layoutRect, int* stringFormat, int* brush);
extern "C" int WINAPI GdipDeleteFont(int* font);
extern "C" int WINAPI GdipDeleteStringFormat(int* format);
extern "C" int WINAPI GdipDeleteFontFamily(int* fontFamily);

int token;
int* pen;
int* brush;
int* linebrush;
int* image;

HDC mdc;

//*************************************************************
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

WNDCLASS wc;
const TCHAR* AppName = TEXT("MyWindowClass1");
HWND hwnd1;

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPTSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	//GDI+开启
	GdiplusStartupInput StartupInput = { 0 };
	StartupInput.GdiplusVersion = 1;
	if (GdiplusStartup(&token, &StartupInput, NULL))MessageBox(0, TEXT("GdiPlus开启失败"), TEXT("错误"), MB_ICONERROR);

	//这里是在构建窗口类结构
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;//窗口回调函数指针
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;//实例句柄
	wc.hIcon = LoadIcon(hInstance, TEXT("ICON_1"));
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);//默认指针
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);//默认背景颜色
	wc.lpszMenuName = NULL;
	wc.lpszClassName = AppName;//窗口类名

	//注册窗口类
	if (!RegisterClass(&wc))
	{
		MessageBox(NULL, TEXT("注册窗口类失败!"), TEXT("错误"), MB_ICONERROR);
		return 0;
	}

	//创建窗口
	int style = WS_OVERLAPPEDWINDOW;
	hwnd1 = CreateWindowEx(NULL, AppName, TEXT("窗口标题"), style, 50, 50, 500, 500, 0, LoadMenu(hInstance, TEXT("MENU1")), hInstance, 0);
	if (hwnd1 == NULL)
	{
		MessageBox(NULL, TEXT("创建窗口失败!"), TEXT("错误"), MB_ICONERROR);
		return 0;
	}
	//无边框窗口
	SetWindowLong(hwnd1, GWL_STYLE, WS_OVERLAPPED | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);

	//显示、更新窗口
	ShowWindow(hwnd1, nCmdShow);
	UpdateWindow(hwnd1);

	//消息循环
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	//GDI+关闭
	GdiplusShutdown(token);//可以把这个写在消息循环后面,程序退出就销毁,或者在不需要GDI+时调用,比如GDI+窗口的WM_DESTROY消息里调用
	return msg.wParam;

}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg){
	case WM_PAINT:
		HDC hdc;
		PAINTSTRUCT ps;
		hdc = BeginPaint(hwnd, &ps);

		int graphics;
		GdipCreateFromHDC(mdc, &graphics);//创建Graphics对象

		GdipCreateSolidFill(0xFFFFFFFF, &brush);
		GdipFillRectangle(graphics, brush, 0, 0, 500, 500);
		GdipDeleteBrush(brush);

		GdipCreateSolidFill(0x900000FF, &brush);//创建单色画刷
		PointF p1; p1 = { 0, 0 };
		PointF p2; p2 = { 280, 20 };
		GdipCreateLineBrush(&p1, &p2, 0xFFFF0000, 0xB00000FF, 0, &linebrush);//创建线性渐变画刷

		GdipSetTextRenderingHint(graphics, 3);//设置Graphics对象的文本渲染模式
		int* fontfamily;
		GdipSetSmoothingMode(graphics, 4);//设置Graphics对象的渲染质量

		GdipCreateFontFamilyFromName(L"宋体", NULL, &fontfamily);//创建一个基于指定的字体系列的FontFamily对象
		int* format;
		GdipCreateStringFormat(0, 0, &format);//创建一个基于字符串的格式标志和语言的 StringFormat 对象
		GdipSetStringFormatAlign(format, 0);//设置文本对齐方式
		int* font;
		GdipCreateFont(fontfamily, 14, 0, 2, &font);//创建字体
		RectF rect; rect = { 20, 20, 280, 20 };
		GdipDrawString(graphics, L"I love Win32 and GdiplusFlat", -1, font, &rect, 0, brush);//画文字
		rect = { 20, 60, 280, 20 };
		GdipDrawString(graphics, L"I love Win32 and GdiplusFlat", -1, font, &rect, 0, linebrush);
		GdipDeleteFont(font);//销毁指定Font对象,释放资源
		GdipDeleteStringFormat(format);//销毁指定StringFormat对象,释放资源
		GdipDeleteFontFamily(fontfamily);//销毁指定FontFamily对象,释放资源

		GdipDeleteBrush(brush);//销毁画刷
		GdipDeleteBrush(linebrush);//销毁画刷

		p1 = { 0, 0 };
		p2 = { 400, 120 };
		GdipCreateLineBrush(&p1, &p2, RGB(164, 73, 163) | 0xFF000000, RGB(232, 162, 0) | 0xA0000000, 0, &linebrush);//创建线性渐变画刷
		GdipCreateFontFamilyFromName(L"华文新魏", NULL, &fontfamily);
		GdipCreateStringFormat(0, 0, &format);
		GdipSetStringFormatAlign(format, 0);
		GdipCreateFont(fontfamily, 30, 0, 2, &font);
		rect = { 20, 100, 400, 120 };
		GdipDrawString(graphics, L"醉时空欢,是一种态度。忘却烦恼,用最真诚的心写最朴实的代码", -1, font, &rect, 0, linebrush);
		GdipDeleteFont(font);
		GdipDeleteStringFormat(format);
		GdipDeleteFontFamily(fontfamily);

		GdipDeleteBrush(linebrush);//销毁画刷

		StretchBlt(hdc, 0, 0, 500, 500, mdc, 0, 0, 500, 500, SRCCOPY);//DC复制

		BLENDFUNCTION blend; blend = { AC_SRC_OVER, 0, 150, AC_SRC_ALPHA };
		AlphaBlend(hdc, 20, 200, 200, 200, mdc, 0, 0, 200,200, blend);//透明复制

		GdipDeleteGraphics(graphics);//销毁Graphics对象

		EndPaint(hwnd, &ps);
		return 0;//告诉系统,WM_PAINT消息我已经处理了,你那儿凉快哪儿玩去吧。
	case WM_CREATE:
		mdc = CreateCompatibleDC(0);
		BITMAPINFO bitmapinfo;
		RtlZeroMemory(&bitmapinfo, sizeof(BITMAPINFO));

		bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bitmapinfo.bmiHeader.biWidth = 500;
		bitmapinfo.bmiHeader.biHeight = 500;
		bitmapinfo.bmiHeader.biPlanes = 1;
		bitmapinfo.bmiHeader.biBitCount = 32;
		
		HBITMAP b,old;
		b = CreateDIBSection(0, &bitmapinfo, 0, 0, 0, 0);
		old = (HBITMAP)SelectObject(mdc, b);
		if (old != NULL){
			DeleteObject(old);
		}
		DeleteObject(b);
		break;
	case WM_DESTROY://窗口已经销毁
		DeleteDC(mdc);
		PostQuitMessage(0);//退出消息循环,结束应用程序
		return 0;
		break;
	case WM_LBUTTONDOWN://鼠标左键按下
		//让无边框窗口能够拖动(在窗口客户区拖动)
		PostMessage(hwnd, WM_SYSCOMMAND, 61458, 0);
		break;
		/*case WM_MOUSEMOVE://鼠标移动
			int xPos, yPos;
			xPos = GET_X_LPARAM(lParam);//鼠标位置X坐标
			yPos = GET_Y_LPARAM(lParam);//鼠标位置Y坐标
			//不要用LOWORD和HIWORD获取坐标,因为坐标有可能是负的
			break;*/
	default:
		break;
	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);//其他消息交给系统处理
}

效果图:

GdiplusFlat(7)双缓冲绘图和DC透明复制_第3张图片




你可能感兴趣的:(Win32,windows,api,GDI+,gdiplus)