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

来源:互联网 发布:知乎回答不了问题 编辑:程序博客网 时间:2024/05/18 08:42
本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:http://blog.csdn.net/zuishikonghuan/article/details/47316261

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

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

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

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

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

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

何谓“双缓冲绘图”?

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

      

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

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

双缓冲绘图(如右图所示),是指创建一个和窗口的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+Flattypedef 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);//其他消息交给系统处理}

效果图:




2 0
原创粉丝点击