GUI绘图

简单图形绘制

在win32上面绘制图形其实是比较简单的。要想进行图形绘制,关键是找到设备上下文。就我目前所知道的,获得设备上下文主要有这么几种方式:一、根据BeginPaint获得设备上下文,这主要在WM_PAINT使用;二、根据GetDC获得设备上下文,这主要用在非WM_PAINT消息中;三、CreateDC获得设备上下文,这个主要用在屏幕的截取;四、根据CreateCompatibleDC获得设备上下文,然后通过BitBlt进行绘制,这个一般用在图形界面较多的绘制场景。

WM_PAINT下用BeginPaint获得设备上下文是比较常用的方式,比如下面一段代码

		case WM_PAINT:
		    hdc = BeginPaint(hWnd, &ps);
			// TODO: Add any drawing code here...
			MoveToEx(hdc, 100,100,NULL);
			LineTo(hdc, 150,230);
			Rectangle(hdc, 200, 200, 300, 300);
			Ellipse(hdc, 15,15,  130, 130);
			EndPaint(hdc, &ps);
			break;

同样一段代码,如果是在鼠标响应的时候调用

			case WM_RBUTTONUP:
			    hdc = GetDC(hWnd);
			    // TODO: Add any drawing code here...
			    MoveToEx(hdc, 100,100,NULL);
			    LineTo(hdc, 150,230);
			    Rectangle(hdc, 200, 200, 300, 300);
			    Ellipse(hdc, 15,15,  130, 130);
			    ReleaseDC(hWnd, hdc);
			    break;

画笔和画刷

要绘制出漂亮的图形,需要用到画笔和画刷了,简单来说,画笔是用来画线和边框的,那画刷就是用来填充那些封闭图形的。画笔能够控制线条的颜色、样式、大小等,画刷能够控制填充的类型、颜色、方式等。下面介绍画笔画刷的创建和使用,还有各种图形的绘制。颜色都用RGB进行设置。矩形、椭圆、弓形、扇形、多边形等这些是填充图形,都用当前的画笔画轮廓,用当前的画刷和多边形填充模式画填充。在绘制多条折线或多个多边形时,顶点数组定义时,需要注意:顶点是连续定义的,也按一笔画绘制。

画笔画刷的使用步骤:(两者使用一致)

  1. 创建画笔或画刷。
  2. 选取画笔或画刷(选入设备环境,记得保留先前画笔和画刷)(用SelectObject,此函数返回值为先前画笔画刷)。
  3. 使用绘图函数进行绘图。
  4. 删除自己所创建的画笔或画刷。(用DeleteObject)

用到的API接口

BeginPaint 函数为指定窗口进行绘图工作的准备,并用将和绘图有关的信息填充到一个PAINTSTRUCT结构中。

HDC BeginPaint(
  [in]  HWND          hWnd,			//窗口句柄
  [out] LPPAINTSTRUCT lpPaint		// 绘制信息
);

EndPaint 函数标记指定窗口的绘画过程结束;这个函数在每次调用BeginPaint函数之后被请求,但仅仅在绘画完成以后。

BOOL EndPaint(
HWND hWnd, // 窗口句柄
CONST PAINTSTRUCT *lpPaint // 绘制窗口的数据
);

MoveToEx 是函数,功能是将当前绘图位置移动到某个具体的点,同时也可获得之前位置的坐标。

BOOL MoveToEx(
  [in]  HDC     hdc,   //传入参数,设备上下文句柄。
  [in]  int     x,		//传入参数:新位置的X坐标。
  [in]  int     y,		//传入参数:新位置的Y坐标。
  [out] LPPOINT lppt    //传出参数:一个指向POINT结构的指针,用来存放上一个点的位置,若此参数为NULL,则不保存上一个点的位置
);

LineTo 用当前画笔画一条线,从当前位置连到一个指定的点。这个函数调用完毕,当前位置变成x,y。

BOOL LineTo(
  [in] HDC hdc, //设备场景句柄
  [in] int x, //线段终点X坐标位置,采用逻辑坐标表示。这个点不会实际画出来;它不属于线段的一部份
  [in] int y //线段终点Y坐标位置,采用逻辑坐标表示。这个点不会实际画出来;它不属于线段的一部份
);

Rectangle 是一个函数,使用该函数画一个矩形,可以用当前的画笔画矩形轮廓,用当前画刷进行填充。

BOOL Rectangle(
  [in] HDC hdc, //设备环境句柄。
  [in] int left, //指定矩形左上角的逻辑X坐标。
  [in] int top, //指定矩形左上角的逻辑Y坐标。
  [in] int right, //指定矩形右下角的逻辑X坐标。
  [in] int bottom //指定矩形右下角的逻辑Y坐标。
);

Ellipse 画一个椭圆。圆心是标识的矩形的中心。椭圆轮廓会被设备环境当前画笔绘画,同时椭圆被当前画刷填充。

BOOL Ellipse(
  [in] HDC hdc,   //设备环境句柄。
  [in] int left,   //矩形左上角横座标
  [in] int top, 	//矩形左上角纵座标
  [in] int right,  //矩形右角横座标
  [in] int bottom  //矩形右下角纵座标
);

SetPixel 该函数将指定坐标处的像素设为指定的颜色。

COLORREF SetPixel(
  [in] HDC      hdc,  //设备环境句柄。
  [in] int      x,  //指定要设置的点的X轴坐标,按逻辑单位表示坐标。
  [in] int      y,  //指定要设置的点的Y轴坐标,按逻辑单位表示坐标。
  [in] COLORREF color  //指定要用来绘制该点的颜色。
);

GetPixel 函数功能,该函数检索指定坐标点的像素的RGB颜色值。

COLORREF GetPixel(
  [in] HDC hdc, //设备环境句柄。
  [in] int x, //指定要检查的像素点的逻辑X轴坐标。
  [in] int y //指定要检查的像素点的逻辑Y轴坐标。
);

Polygon 根据顶点绘制多边形

 BOOL Polygon( HDC hdc,
               CONST POINT *lpPoints, // 多边形的顶点
               int nCount );          // 顶点的数量
// DrawPic.cpp : 定义应用程序的入口点。
//

#include "framework.h"
#include "DrawPic.h"

#define MAX_LOADSTRING 100

// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此处放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_DRAWPIC, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DRAWPIC));

    MSG msg;

    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DRAWPIC));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_DRAWPIC);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目标: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目标: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
	HPEN hpen1, oldhpen, hpen2, hpen3, hpen4, hpen5, hpen;
	HBRUSH hbrush1, oldhbrush, hbrush2, hbrush3, hbrush4, hbrush5, hbrush6;
	static int xClient, yClient;
	static POINT apt[5] = { 90,50,110,10,130,50,150,10,180,50 };//5个点
	static POINT capt[] = { 200, 50, 230, 10, 260, 50, 290, 10, 310, 50,
					200, 100, 230, 60, 260, 100, 290, 60, 310, 100,
					200, 150, 230, 110, 260, 150, 290, 110, 310, 150 };
	static DWORD asz[] = { 5,5,5 };
	static POINT bapt[4] = { 350,50,350,80,370,50,450,10 };//4个点

    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
	case WM_SIZE:
	{
		xClient = LOWORD(lParam);
		yClient = HIWORD(lParam);
		break;
	}
    case WM_PAINT:
        {
		hdc = BeginPaint(hWnd, &ps);
		//还可以使用CreatePenIndirect创建画笔
		//画一个红色点
		SetPixel(hdc, 20, 50, RGB(255, 0, 0));

		//画一条直线
		hpen1 = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));//实线
		oldhpen = (HPEN)SelectObject(hdc, hpen1);//选入画笔
		MoveToEx(hdc, 30, 50, NULL);
		LineTo(hdc, 70, 50);
		SelectObject(hdc, oldhpen);//恢复先前画笔
		DeleteObject(hpen1);//记得自创画笔一定删除

		//画一条5个点折线
		hpen2 = CreatePen(PS_DASH, 1, RGB(255, 255, 0));//段线
		oldhpen = (HPEN)SelectObject(hdc, hpen2);//选入画笔
		Polyline(hdc, apt, 5); //其后缀不带To,表示该函数不使用也不改变设备当前位置
		SelectObject(hdc, oldhpen);//恢复先前画笔
		DeleteObject(hpen2);//记得自创画笔一定删除

		//画3条折线
		hpen3 = CreatePen(PS_DASHDOT, 1, RGB(255, 0, 255));//线点
		SetBkColor(hdc, RGB(0, 0, 222));// 设置当前背景颜色
		oldhpen = (HPEN)SelectObject(hdc, hpen3);//选入画笔
		PolyPolyline(hdc, capt, asz, 3);
		SelectObject(hdc, oldhpen);//恢复先前画笔
		DeleteObject(hpen3);//记得自创画笔一定删除

		//画贝塞尔曲线(最少需要4个点)
		hpen4 = (HPEN)GetStockObject(BLACK_PEN);//获得Windows预设的画笔画刷
		oldhpen = (HPEN)SelectObject(hdc, hpen4);//选入画笔
		PolyBezier(hdc, bapt, 4);// 4个点: 1和4是端点, 2.3点是控制点
								 // 7个点 : 1.4.7是端点, 其余是控制点
		SelectObject(hdc, oldhpen);//恢复先前画笔
		DeleteObject(hpen4);//记得自创画笔一定删除

		//画无边框矩形
		hbrush1 = CreateSolidBrush(RGB(0, 255, 0)); //绿色实心画刷
		hpen = CreatePen(PS_NULL, 1, 0);  //创建空画笔
		oldhpen = (HPEN)SelectObject(hdc, hpen);  //重点:填充图形的边框由当前画笔绘制
		oldhbrush = (HBRUSH)SelectObject(hdc, hbrush1);//选入环境句柄
		Rectangle(hdc, 10, 80, 70, 140);
		SelectObject(hdc, oldhpen);
		SelectObject(hdc, oldhbrush);
		DeleteObject(hbrush1);//删除画刷

		//圆角矩形
		SetBkColor(hdc, RGB(0, 0, 0));// 设置当前背景颜色
		hbrush2 = CreateHatchBrush(HS_FDIAGONAL, RGB(255, 255, 0));//创建画刷
		oldhbrush = (HBRUSH)SelectObject(hdc, hbrush2);//选入设备环境
		RoundRect(hdc, 90, 80, 150, 140, 20, 20);//画图
		SelectObject(hdc, oldhbrush);//恢复先前画刷
		DeleteObject(hbrush2);//删除画刷

		//椭圆
		SetBkColor(hdc, RGB(222, 0, 0));// 设置当前背景颜色
		hbrush3 = CreateHatchBrush(HS_CROSS, RGB(255, 255, 0));
		oldhbrush = (HBRUSH)SelectObject(hdc, hbrush3);
		Ellipse(hdc, 10, 160, 70, 200);
		SelectObject(hdc, oldhbrush);
		DeleteObject(hbrush3);

		//普通弧
		SetDCPenColor(hdc, RGB(255, 0, 255));  //设置句柄颜色
		hpen5 = (HPEN)GetStockObject(DC_PEN);
		oldhpen = (HPEN)SelectObject(hdc, hpen5);
		Arc(hdc, 90, 160, 150, 200, 90, 180, 150, 170);
		SelectObject(hdc, oldhpen);
		DeleteObject(hpen5);
		//圆弧
		MoveToEx(hdc, 190, 200, NULL);
		AngleArc(hdc, 190, 200, 30, 10, 90);  //终点会与当前位置连接起来

		//弓形、弦
		hbrush4 = CreateSolidBrush(RGB(155, 155, 155));
		oldhbrush = (HBRUSH)SelectObject(hdc, hbrush4);
		Chord(hdc, 240, 160, 300, 240, 270, 180, 290, 230);
		SelectObject(hdc, oldhbrush);
		DeleteObject(hbrush4);

		//扇形
		SetBkColor(hdc, RGB(35, 55, 111));// 设置当前背景颜色
		hbrush5 = CreateHatchBrush(HS_HORIZONTAL, RGB(0, 255, 0));
		oldhbrush = (HBRUSH)SelectObject(hdc, hbrush5);
		Pie(hdc, 330, 160, 370, 220, 333, 26, 370, 220);
		SelectObject(hdc, oldhbrush);
		DeleteObject(hbrush5);

		//绘制多边形,
		POINT dot[5]{ 100,300,200,300,125,350,150,250,175,350 };//按笔画顺序绘制5个点
		SetPolyFillMode(hdc, ALTERNATE);//设置填充模式
		hbrush6 = CreateSolidBrush(RGB(0, 255, 0)); //绿色实心画刷
		oldhbrush = (HBRUSH)SelectObject(hdc, hbrush6);
		Polygon(hdc, dot, 5);//绘制多边形
		SelectObject(hdc, oldhbrush);
		DeleteObject(hbrush6);



		EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

位图绘制

绘制位图步骤:

  1. 创建兼容DC(CreateCopatibleDC函数)
  2. 加载位图(LoadImage函数和LoadBitmap函数)
  3. 选取位图对象,使用BitBlt函数进行贴图(SelectObject函数)
  4. 释放兼容DC(DeleteDC函数)

加载位图API函数:

LoadImage
LoadImage是一种函数,功能是装载图标,光标,或位图。

HANDLE LoadImage(HINSTANCE hinst,// 处理包含被装载图像模块的实例句柄。若要装载OEM图像,则设此参数值为0。
                  LPCTSTR lpszName,// 图像资源名称
                  UINT uType,   // 指定被装载图像类型,IMAGE_BITMAP:装载位图;IMAGE_CURSOR:装载光标;IMAGE_ICON:装载图标。
                  int cxDesired,// 指定图标或光标的宽度,以像素为单位
                  int cyDesired,// 指定图标或光标的高度,以像素为单位
                  UINT fuLoad);// 加载标识,含义见链接

LoadBitmap
该函数从模块的可执行文件中加载指定的位图资源,该函数已经被函数LoadImage替代。

void DrawBitMap(HDC hdc)
{
	//创建兼容DC
	HDC hdc_bmp = CreateCompatibleDC(hdc);

	//加载位图
	HBITMAP map = (HBITMAP)LoadImage(NULL, _TEXT("H:\\植物大战僵尸素材\\interface\\background1.bmp")
		, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

	//选取位图对象,是用BitBlt函数贴图
	SelectObject(hdc_bmp, map);
	BitBlt(hdc, 0, 0, 1400, 600, hdc_bmp, 0, 0, SRCCOPY);

	//释放兼容DC
	DeleteDC(hdc_bmp);
}

双缓冲绘制

为什么需要使用双缓冲技术?可能很多朋友会问,不知道你们有没有发现,当屏幕刷新的时候会有闪烁,这样让人的体验感极差。原因是绘图与显示器刷新不同步,有时间差,为解决这一问题,这就需要用到双缓冲技术来绘图了。双缓冲技术是相对单缓冲而言的,单缓冲就是直接在设备DC上绘图;而双缓冲就是先在一个与设备DC相兼容的内存缓冲区里进行绘图,然后再一次性复制到设备DC上。一次性在屏幕上显示就不会出现闪烁的现象。

这里需要注意的是:我们创建的兼容DC,不能直接在上面绘图,这里还需要一块画布,那我们创建的兼容DC就相当于画板,有了画板、画布,将画布选放在画板兼容DC上就可以进行绘图了。然后一次性贴在设备DC上就搞定了。如下:

HDC mdc=CreateComatibleDC(hdc);   // 创建兼容DC 画板
HBITMAP bmp=CreatrCompatibleBitnap(hdc,600,600);   // 创建画布
SelectObject(mdc,bmp);   //  将画布选入画板

双缓冲绘图步骤:

  1. 在内存中创建兼容DC缓冲区(依次包括创建兼容DCCreateComatibleDC、创建画布CreatrCompatibleBitnap、将画布选入SelectObject)。
  2. 在缓冲区进行画图操作(可以画图形、也可以贴位图)。
  3. 将兼容缓冲区一次性复制到设备DC上。(复制用Bitblt函数)
  4. 释放内存缓冲区。(DeleteDC函数)

CreateCompatibleDC:
该函数创建一个与指定设备兼容的内存设备上下文环境(DC)

 HDC CreateCompatibleDC(HDC hdc);

CreateCompatibleBitmap:
该函数创建与指定的设备环境相关的设备兼容的位图.

HBITMAP CreateCompatibleBitmap(HDC hdc,  
                               int nWidth,   // 定位图的宽度,单位为像素
                               int nHeight)// 指定位图的高度,单位为像素

BitBlt:
BitBlt是一个计算机函数,该函数对指定的源设备环境区域中的像素进行位块(bit_block)转换,以传送到目标设备环境.

BOOL BitBlt(  _In_  HDC hdcDest, 
              _In_  int nXDest,  // 指定目标矩形区域左上角的X轴逻辑坐标
              _In_  int nYDest,  // 指定目标矩形区域左上角的Y轴逻辑坐标
              _In_  int nWidth,  // 指定源在目标矩形区域的逻辑宽度
              _In_  int nHeight, // 指定源在目标矩形区域的逻辑高度
              _In_  HDC hdcSrc,  // 指向源设备环境的句柄
              _In_  int nXSrc,   // 指定源矩形区域左上角的X轴逻辑坐标
              _In_  int nYSrc,   // 指定源矩形区域左上角的Y轴逻辑坐标
              _In_  DWORD dwRop); //指定光栅操作代码。这些代码将定义源矩形区域的颜色数据,如何与目标矩形区域的颜色数据组合以完成最后的颜色。

单双缓冲技术操作对比:

单缓冲技术绘制两个矩形:

// 直接在设备DC上绘制
Rectangle(hdc, 100, 100, 200, 200);
Rectangle(hdc, 300, 300, 200, 200);

双缓冲技术绘制两个矩形:

// 1.创建兼容缓冲区
mdc = CreateCompatibleDC(hdc);   // 创建兼容DC
bmp = CreateCompatibleBitmap(hdc, 600, 600);   // 创建兼容位图画布
SelectObject(mdc, bmp);    // 选入

// 2.在缓冲区绘制 
Rectangle(mdc, 100, 100, 200, 200);
Rectangle(mdc, 300, 300, 200, 200);

// 3.一次性复制到设备DC
BitBlt(hdc, 0, 0, 500, 500, mdc, 0, 0, SRCCOPY);
 
// 4.释放缓冲区DC
DeleteDC(mdc);

单缓冲绘制位图:

// 1.创建兼容DC
maphdc = CreateCompatibleDC(hdc);

// 2.加载位图
HBITMAP map = (HBITMAP)LoadImage(hInst, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 500, 500, LR_CREATEDIBSECTION);

// 3.选取位图对象,使用BitBlt函数贴图
SelectObject(maphdc, map);
BitBlt(hdc, 0, 0, 1500, 1500, maphdc, 0, 0, SRCCOPY);

// 4.释放兼容DC
DeleteDC(maphdc);

双缓冲绘制位图:

void DrawTest(HDC hdc)
{
	// 1.创建兼容缓冲区
	HDC mdc = CreateCompatibleDC(hdc);   // 创建兼容DC
	HBITMAP bmp = CreateCompatibleBitmap(hdc, 1400, 600);   // 创建兼容位图画布
	SelectObject(mdc, bmp);    // 选入

	// 2.在缓冲区绘制 
	HDC maphdc = CreateCompatibleDC(hdc); /*创建兼容DC*/
	/*贴第一张位图*/
	HBITMAP map1 = (HBITMAP)LoadImage(NULL, _TEXT("H:\\植物大战僵尸素材\\interface\\background1.bmp")
		, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
	SelectObject(maphdc, map1);
	BitBlt(mdc, 0, 0, 1400, 600, maphdc, 0, 0, SRCCOPY);

	/*贴第二张位图*/
	HBITMAP map2 = (HBITMAP)LoadImage(NULL, _TEXT("H:\\植物大战僵尸素材\\Card\\Plants\\Peashooter.bmp")
		, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
	SelectObject(maphdc, map2);
	BitBlt(mdc, 425, 200, 100, 60, maphdc, 0, 0, SRCCOPY);

	DeleteDC(maphdc);  /*释放兼容DC*/

	// 3.一次性复制到设备DC
	BitBlt(hdc, 0, 0, 1400, 600, mdc, 0, 0, SRCCOPY);

	// 4.释放缓冲区DC
	DeleteDC(mdc);

}

透明位图的显示

TransparentBlt 函数在Windows98/Windows2000以上版本运行,系统中需要包含 Msimg32.dll,使用时可以链接 Msimg32.lib。

BOOL TransparentBlt(
HDC hdcDest,      // 目标DC
int nXOriginDest,   // 目标X偏移
int nYOriginDest,   // 目标Y偏移
int nWidthDest,     // 目标宽度
int hHeightDest,    // 目标高度
HDC hdcSrc,         // 源DC
int nXOriginSrc,    // 源X起点
int nYOriginSrc,    // 源Y起点
int nWidthSrc,      // 源宽度
int nHeightSrc,     // 源高度
UINT crTransparent  // 透明色,COLORREF类型
);

demo

#pragma comment( lib, "MSIMG32.LIB")
void DrawTest(HDC hdc)
{
	// 1.创建兼容缓冲区
	HDC mdc = CreateCompatibleDC(hdc);   // 创建兼容DC
	HBITMAP bmp = CreateCompatibleBitmap(hdc, 1400, 600);   // 创建兼容位图画布
	SelectObject(mdc, bmp);    // 选入

	// 2.在缓冲区绘制 
	HDC maphdc = CreateCompatibleDC(hdc); /*创建兼容DC*/
	/*贴第一张位图*/
	HBITMAP map1 = (HBITMAP)LoadImage(NULL, _TEXT("H:\\植物大战僵尸素材\\interface\\background1.bmp")
		, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
	SelectObject(maphdc, map1);
	BitBlt(mdc, 0, 0, 1400, 600, maphdc, 0, 0, SRCCOPY);

	/*贴第二张位图*/
	HBITMAP map2 = (HBITMAP)LoadImage(NULL, _TEXT("H:\\植物大战僵尸素材\\Zombies\\Zombie\\Zombie2.bmp")
		, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
	SelectObject(maphdc, map2);
	TransparentBlt(mdc, 430, 200, 166, 144, maphdc, 0, 0, 166, 144, RGB(255, 255, 255));

	DeleteDC(maphdc);  /*释放兼容DC*/

	// 3.一次性复制到设备DC
	BitBlt(hdc, 0, 0, 1400, 600, mdc, 0, 0, SRCCOPY);

	// 4.释放缓冲区DC
	DeleteDC(mdc);

}

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值