《Windows程序设计》读书笔五 绘图基础

本文详细介绍了Windows图形设备接口(GDI)的基本概念和技术细节,包括GDI的工作原理、常用的GDI函数及其分类,基本图形的绘制方法,以及设备环境的相关操作。此外,还深入探讨了如何使用GDI进行点、线、填充区域的绘制,并提供了多个实例代码帮助理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Windows在基于图形设备接口的视频显示器和打印机上显示图像。GDI

5.1.1 GDI原理

GDI是为了兼容于设备无关的图形。

图形输出设备分为两大类,光栅设备和矢量设备。GDI是图形设备硬件的一种高级接口。

GDI总的来说是一个静态系统,对动画支持有限。如果需要为游戏编写复杂的动画,还是需要使用DirectX 或者OpenGL


5.1.2 GDI函数使用

GDI函数的分类

*获取和释放设备环境的函数 BeginPaint/ EndPaint  GetDC/ Release DC

*获取设备环境信息的函数 GetTextMetrics

*绘图函数

*设置和获取设备环境属性的函数 Set* Get*

*使用GDI“对象”的函数


5.1.3 GDI的基本图形

*线条和曲线, 

  直线,矩形,椭圆,椭圆弧线,贝塞尔曲线。GDI使用当前选入设备环境的画笔绘制线条。

*可被填充的封闭区域

  GDI使用画刷对象填充封闭的区域。可以使纯色,某种填充模式也可以是不停重复的位图。

* 位图

   二维的位数组对应显示设备上的一个像素。GDI支持设备相关位图和设备无关位图DIB

* 文本

和印刷相关,GDI中文本处理最庞大。


5.1.4 其他

其他GDI函数

* 映射模式和转换 坐标转换,图形旋转等。

* 图元文件(metafile)是以二进制形式存储的GDI命令的集合。主要通过剪贴板转换矢量图形绘制的表现形式

* 区域(region)

* 路径 GDI内部的直线和曲线的集合

* 剪裁 当绘图被限制在特定的区域就称为剪裁,可以是矩形或者非矩形。 通常被指定为一个区域或者一个路径

* 调色板 仅支持256色时才能使用自定义调色板。windows保留20种,你可以改变其他236种色彩

* 打印


5.2 设备环境

要在图形设备上绘制图形,必须获得设备环境的句柄。(DC)。windows把这个句柄交给你的程序,也就交给你使用权。接着将GDI函数中将这个句柄作为一个参数,

告诉windows在哪个设备上进行绘图。


5.2.1 获取设备环境的句柄

最常用方法是在WM_PAINT消息

hdc = BeginPaint(hwnd, &ps);

EndPaint(hwnd, *ps);

在处理非WM_PAINT消息时由windows程序获取

hdc = GetDC(hwnd);

ReleaseDC(hwnd, hdc); 可以绘制整个客户区,但是并不使得客户区从无效变为有效。

还可以获得整个窗口的设备环境

hdc = GetWindowDC(hwnd);

ReleaseDC(hwnd, hdc); 如果使用这个还应该捕获WM_NCPAINT 非客户区绘制信息


以上获取的DC可以在视频显示器上与一个特定的窗口相关联的设备环境。  还有一个更通用的用于获取设备环境句柄的函数是

CreateDC

hdc = CreateDC(pszDriver, pszDevice, pszOutput, pData);

DeleteDC(hdc);


例如可以获得当前整个屏幕的设备环境句柄

hdc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL); 

打印机的设备环境句柄就是用CreateDC函数来获取的。


有时候仅需要获取设备环境的信息,而不需要绘制任何东西。可以用CreateIC获取 信息上下文句柄。

hdc = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL); 


处理位图时,有时候可能会用到一个“内存设备环境”

hdcMem = CreateCompatibleDC(hdc);

DeleteDC(hdcMem);


图元文件是二进制编码的GDI函数调用集合,可以获取一个图元文件的设备环境

hdcMeta = CreateMetaFile(pszFilename);

hmf = CloseMetaFile(hdcMeta);

使用hdcMeta所做的任何GDI调用都不会显示出来,他会成为图元文件的一部分。


5.2.2 获取设备环境的信息

设备环境通常指的是物理显示设备,如视频显示器或者打印机。

iValue = GetDeviceCaps(hdc, iIndex);


5.2.3 DEVCAPS1 程序

DEVCAPS1 程序显示了在使用视频显示设备环境时,可以从GetDeviceCaps函数得到的部分信息。

#include <windows.h>

#define NUMLINES ((int) (sizeof devcaps / sizeof(devcaps[0])))

struct 
{
	int		iIndex;
	TCHAR*	szLabel;
	TCHAR*	szDesc;
}
devcaps[] =
{
	HORZSIZE,      TEXT("HORZSIZE"),     TEXT("Width in millimeters:"),
	VERTSIZE,      TEXT("VERTSIZE"),     TEXT("Height in millimeters:"),
	HORZRES,       TEXT("HORZRES"),      TEXT("Width in pixels:"),
	VERTRES,       TEXT("VERTRES"),      TEXT("Height in raster lines:"),
	BITSPIXEL,     TEXT("BITSPIXEL"),    TEXT("Color bits per pixel:"),
	PLANES,        TEXT("PLANES"),       TEXT("Number of color planes:"),
	NUMBRUSHES,    TEXT("NUMBRUSHES"),   TEXT("Number of device brushes:"),
	NUMPENS,       TEXT("NUMPENS"),      TEXT("Number of device pens:"),
	NUMMARKERS,    TEXT("NUMMARKERS"),   TEXT("Number of device markers:"),
	NUMFONTS,      TEXT("NUMFONTS"),     TEXT("Number of device fonts:"),
	NUMCOLORS,     TEXT("NUMCOLORS"),    TEXT("Number of device colors:"),
	PDEVICESIZE,   TEXT("PDEVICESIZE"),  TEXT("Size of device structure:"),
	ASPECTX,       TEXT("ASPECTX"),      TEXT("Relative width of pixel:"),
	ASPECTY,       TEXT("ASPECTY"),      TEXT("Relative height of pixel:"),
	ASPECTXY,      TEXT("ASPECTXY"),     TEXT("Relative diagonal of pixel:"),
	LOGPIXELSX,    TEXT("LOGPIXELSX"),   TEXT("Horizontal dots per inch:"),
	LOGPIXELSY,    TEXT("LOGPIXELSY"),   TEXT("Vertical dots per inch:"),
	SIZEPALETTE,   TEXT("SIZEPALETTE"),  TEXT("Number of palette entries:"),
	NUMRESERVED,   TEXT("NUMRESERVED"),  TEXT("Reserved palette entries:"),
	COLORRES,      TEXT("COLORRES"),     TEXT("Actual color resolution:")
};

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static		TCHAR szAppName[] = TEXT("DevCaps1");
	HWND		hwnd;
	MSG			msg;
	WNDCLASS	wndClass;		//The window Class

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System. 
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.
	hwnd = CreateWindow(szAppName,		//Window class name
		TEXT("Device Capabilities"),		//Window caption
		WS_OVERLAPPEDWINDOW,			//Window Style
		CW_USEDEFAULT,					//initial x position
		CW_USEDEFAULT,					//initial y position
		CW_USEDEFAULT,					//initial x size
		CW_USEDEFAULT,					//initial y size
		NULL,							//parent window handle
		NULL,							//window menu handle
		hInstance,						//program instance handle
		NULL);							//creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);	//This function will generate a WM_PAINT message.

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static int	cxChar, cxCaps, cyChar;
	TCHAR		szBuffer[10];
	HDC			hdc;
	int			i;
	PAINTSTRUCT	ps;
	TEXTMETRIC	tm;

	switch (message) //get the message
	{
	case WM_CREATE:
		hdc = GetDC(hwnd);

		GetTextMetrics(hdc, &tm);
		cxChar = tm.tmAveCharWidth;
		cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
		cyChar = tm.tmHeight + tm.tmExternalLeading;

		ReleaseDC(hwnd, hdc);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		for (i = 0; i < NUMLINES; ++i)
		{
			TextOut(hdc, 0, cyChar * i,
				devcaps[i].szLabel,
				lstrlen(devcaps[i].szLabel));
			TextOut(hdc, 14 * cxCaps, cyChar*i,
				devcaps[i].szDesc,
				lstrlen(devcaps[i].szDesc));
			SetTextAlign(hdc, TA_RIGHT | TA_TOP);

			TextOut(hdc, 14 * cxCaps + 35 * cxChar, cyChar * i,
				szBuffer,
				wsprintf(szBuffer, TEXT("%5d"),
					GetDeviceCaps(hdc, devcaps[i].iIndex)));

			SetTextAlign(hdc, TA_LEFT | TA_TOP);
		}
		EndPaint(hwnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

显示结果



5.2.4 设备的尺寸

当你需要显示1英寸的正方形,GetDevieCaps函数可以帮助获取有关输出设备的物理尺寸信息。


本书约定,分辨率指 没度量单位中含的像素数。像素规模 1024x768 之类

可以使用GetSystemMetrics函数时使用SM_CXSCREEN 和SM_CYSCREEN参数来获取显示器的像素规模。

或者GetDeviceCaps 使用HORZRES 和VERTRES 参数来获得同样的值。


传统排版中,字体字符的大小是由“点值”来表示的,1点大约是1/72英寸。在计算机排版中,1点通常假定正好是1/72英寸。

理论上来说,一个字体的点值是从合格字体中,最高字符的顶部到下部字符底端的距离,这里不包括重音符号。例如,10磅的字体从上到下的

距离是10/72英寸。 用TEXTMETRIC结构的术语来说,字号等于字段tmHeight 减去 tmInternalLeading.


windows系统字体都被假定是一个10点的字并使用12点的行间距。

在Windows程序中,通过调用GetDeviceCaps函数,可以获取用户在控制面板程序中选择以每英寸的橡树点数为单位的假定的分辨率。

为了获取这些值,需要LOGPIXELSX 和LOGPIXELSY 逻辑像素。以每英寸的像素计算的非实际分辨率。



5.2.5 色彩ABC

真彩色  full color 24bit  RGB

高彩色  16bit  5bitR 6bitG 5bitB

256色 索引色

16色


色彩平面的数目可以由下函数获得

iPlanes = GetDeviceCaps(hdc, PLANES);

每个像素的颜色位数可以由下面的调用获得

iBitsPixel = GetDeviceCaps(hdc, BITSPIXEL); // in this computer it is 32bit

视频适配器支持的色彩数可由一下公式计算:

iColors = 1 << (iPlanes * iBitsPixel);

通过NUMCOLORS参数获取的值可能一样也可能不一样

iColors = GetDeviceCaps(hdc, NUMCOLORS);


在GDI中使用一个32bit长整数表示颜色


在Wingdi.h中有定义

#define RGB(r,g,b)          ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))

在16bit或者256色的视频适配器上,windows采用抖动来仿真。使设备能够显示更多色彩

crPureColor = GetNearestColor(hdc, crColor);


5.2.6 设备环境属性


当一个程序获取一个设备环境句柄时,windows设置所有属性为默认值。

表5-1

设备环境属性

默认值

修改该值的函数

取得该值的函数

Mapping Mode

MM_TEXT

SetMapMode

GetMapMode

Window Origin

(0, 0)

SetWindowOrgEx

OffsetWindowOrgEx

GetWindowOrgEx

Viewport Origin

(0, 0)

SetViewportOrgEx

OffsetViewportOrgEx

GetViewportOrgEx

Window Extents

(1, 1)

SetWindowExtEx

SetMapMode

ScaleWindowExtEx

GetWindowExtEx

Viewport Extents

(1, 1)

SetViewportExtEx

SetMapMode

ScaleViewportExtEx

GetViewportExtEx

Pen

BLACK_PEN

SelectObject

SelectObject

Brush

WHITE_BRUSH

SelectObject

SelectObject

Font

SYSTEM_FONT

SelectObject

SelectObject

Bitmap

None

SelectObject

SelectObject

Current Position

(0, 0)

MoveToEx

LineTo

PolylineTo

PolyBezierTo

GetCurrentPositionEx

Background Mode

OPAQUE

SetBkMode

GetBkMode

Background Color

White

SetBkColor

GetBkColor

Text Color

Black

SetTextColor

GetTextColor

Drawing Mode

R2_COPYPEN

SetROP2

GetROP2

Stretching Mode

BLACKONWHITE

SetStretchBltMode

GetStretchBltMode

Polygon Fill Mode

ALTERNATE

SetPolyFillMode

GetPolyFillMode

Intercharacter Spacing

0

SetTextCharacterExtra

GetTextCharacterExtra

Brush Origin

(0, 0)

SetBrushOrgEx

GetBrushOrgEx

Clipping Region

None

SelectObject

SelectClipRgn

IntersectClipRgn

OffsetClipRgn

ExcludeClipRect

SelectClipPath

GetClipBox


5.2.7 保存设备环境

每次获取hDC 都会初始化,设定好以后释放所有设定都会丢失。

在注册窗口类的时候将窗口样式设定为CS_OWNDC

wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;

现在每个基于此窗口类创建的窗口都有他的私有设备环境,当窗口被销毁时,这个设备环境依然存在。使用CS_OWNDC只需要初始化设备环境一次,例如

case WM_CREATE:

    hdc = GetDC(hwnd);

    //initialize device context attributes

    ReleaseDC(hwnd, hdc);

在改变这些属性以前,他们一直有效。

CS_OWNDC 只影响通过BeginPaint和GetDC获取的设备环境。其他函数如GetWindowDC获取的设备环境不受影响。即使使用了CS_OWNDC  hdc也应该在退出窗口过程前被释放

可以临时保存设备环境的某些属性,然后修改以后再复原继续使用。

int idSaved = SaveDC(hdc);

然后再还原

RestoreDC(hdc, idSaved);

可以在ReleaseDC之前多次调用SaveDC


5.3 点和线的绘制

理论上,所有图形设备驱动程序所需要的是SetPixel和GetPixel,其余一切可以在GDI中使用更高层的例程来处理。问题是性能


5.3.1 像素设定

SetPixel(hdc, x, y, crColor);   //crColor  COLORREF

crColor = GetPixel(hdc, x, y); // 获取指定像素的颜色

5.3.2 直线

LineTo 直线

Polyline  PolylineTo  多条首位相连的直线构成的折线

PolyPolyline 画多条折线

Arc 画椭圆弧线

PolyBezier PolyBezierTo 画贝赛尔曲线

WinNT增加的

ArcTo 和AngleArc 画椭圆弧线

PolyDraw 画多条贝塞尔曲线和首位相连的直线构成的折线


画线以后会填充所画图形的封闭区域

Rectangle 矩形

Ellipse 画椭圆

RoundRect 画圆角矩形

Pie 画椭圆的一部分,看起来像扇形

Chord 画出由弦分割出的部分椭圆,形状呈弓形


设备环境有5个属性会影响这些函数所绘的线条外观:当前画笔位置(LineTo, PolylineTo, PolyBezierTo, ArcTo),画笔,背景模式,背景颜色和绘图模式。


为了画一条直线

MoveToEX(hdc, xBeg, yBeg, NULL); //指定起点,最后一个POINT指针指向当前点

LineTo(hdc, xEnd, yEnd);  //从当前点画线到终点


GetCurrentPositionEx(hdc, &pt); //获取当前的位置

从左上开始在客户区画一个网格

		GetClientRect(hwnd, &rect);
		for (int x = 0; x < rect.right; x += 100)
		{
			MoveToEx(hdc, x, 0, NULL);
			LineTo(hdc, x, rect.bottom);
		}

		for (int y = 0; y < rect.bottom; y += 100)
		{
			MoveToEx(hdc, 0, y, NULL);
			LineTo(hdc, rect.right, y);
		}

画连续的直线

POINT apt[5] = {100, 100, 200, 100, 200, 200, 100, 200, 100, 100};

MoveToEx(hdc, apt[0].x, apt[0].y, NULL);

for(i = 1; i < 5; ++i)
    LineTo(hdc, apt[i].x, apt[i].y);

或者使用

Polyline(hdc, apt, 5);

或者

MoveToEx(hdc, apt[0].x, apt[0].y, NULL);

PolylineTo(hdc, apt+1, 4);

一个绘制正弦曲线的例子

#include <windows.h>
#include <math.h>

#define NUM		1000
#define TWOPI	(2 * 3.14159)

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static		TCHAR szAppName[] = TEXT("SineWave");
	HWND		hwnd;
	MSG			msg;
	WNDCLASS	wndClass;		//The window Class

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc	= WndProc;// assign the window procedure to windows class.
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName	= NULL;
	wndClass.lpszClassName	= szAppName;

	//Register the Window Class to the Windows System. 
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.
	hwnd = CreateWindow(szAppName,		//Window class name
		TEXT("Sine Wave Using Polyline"),		//Window caption
		WS_OVERLAPPEDWINDOW,			//Window Style
		CW_USEDEFAULT,					//initial x position
		CW_USEDEFAULT,					//initial y position
		CW_USEDEFAULT,					//initial x size
		CW_USEDEFAULT,					//initial y size
		NULL,							//parent window handle
		NULL,							//window menu handle
		hInstance,						//program instance handle
		NULL);							//creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);	//This function will generate a WM_PAINT message.

	/* The message loop for this program.
	if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static int	cxClient, cyClient;
	HDC			hdc;
	int			i;
	PAINTSTRUCT	ps;
	POINT		apt[NUM];

	switch (message) //get the message
	{
	case WM_CREATE:
		return 0;
	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		break;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		MoveToEx(hdc, 0, cyClient / 2, NULL);
		LineTo(hdc, cxClient, cyClient / 2);

		for (i = 0; i < NUM; ++i)
		{
			apt[i].x = i * cxClient / NUM;
			apt[i].y = (int)(cyClient / 2 * (1 - sin(TWOPI * i / NUM)));
		}
		Polyline(hdc, apt, NUM);
		EndPaint(hwnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}


计算好所有点的坐标一次性用Polyline函数绘制,因为是在设备驱动层次上实现的,所以比调用1000次LineTo函数要快。


5.3.3 边框绘制函数

画好以后会填充颜色

Rectangle(hdc, xLeft, yTop, xRight, yBottom);

边界偏差(off-by-one)问题。windows在边框内绘图。

考虑一下函数调用

Rectangle(hdc, 1, 1, 5, 4);


严格来说Rectangle并不是一个画线函数,他会填充这个矩形。因为使用白色填充区域所以看不出填充效果。

椭圆形

Ellipse(hdc, xLeft, yTop, xRight, yBottom);


画圆角矩形

RoundRect(hdc, xLeft, yTop, xRight, yBottom, xCornerEllipse, yCornerEllipse);


xCornerEllipse = (xRight - xLeft) / 4;

yCornerEllipse = (yBottom - yTop) / 4;


Arc (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Chord(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Pie(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);


一个LINEDEMO绘制各种线条的例子 矩形,椭圆,圆角矩形和两条直线

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static		TCHAR szAppName[] = TEXT("LineDemo");
	HWND		hwnd;
	MSG			msg;
	WNDCLASS	wndClass;		//The window Class

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc	= WndProc;// assign the window procedure to windows class.
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName	= NULL;
	wndClass.lpszClassName	= szAppName;

	//Register the Window Class to the Windows System. 
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.
	hwnd = CreateWindow(szAppName,		//Window class name
		TEXT("Line Demonstration"),		//Window caption
		WS_OVERLAPPEDWINDOW,			//Window Style
		CW_USEDEFAULT,					//initial x position
		CW_USEDEFAULT,					//initial y position
		CW_USEDEFAULT,					//initial x size
		CW_USEDEFAULT,					//initial y size
		NULL,							//parent window handle
		NULL,							//window menu handle
		hInstance,						//program instance handle
		NULL);							//creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);	//This function will generate a WM_PAINT message.

	/* The message loop for this program.
	if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static int	cxClient, cyClient;
	HDC			hdc;
	PAINTSTRUCT	ps;

	switch (message) //get the message
	{
	case WM_CREATE:
		return 0;
	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		break;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		Rectangle(hdc, cxClient / 8, cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8);

		MoveToEx(hdc, 0, 0, NULL);
		LineTo(hdc, cxClient, cyClient);

		MoveToEx(hdc, 0, cyClient, NULL);
		LineTo(hdc, cxClient, 0);

		Ellipse(hdc, cxClient / 8, cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8);

		RoundRect(hdc, cxClient / 4, cyClient / 4, 3 * cxClient / 4, 3 * cyClient / 4, cxClient / 4, cyClient / 4);

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

执行结果



5.3.4 贝塞尔样条曲线

一条二位贝塞尔样条曲线使用四个点定义:两个端点和两个控点,两个端点表示起点和终点。控点就好像“磁铁”一样把曲线从两个端点间的直线处吸弯。

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static		TCHAR szAppName[] = TEXT("Bezier");
	HWND		hwnd;
	MSG			msg;
	WNDCLASS	wndClass;		//The window Class

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc	= WndProc;// assign the window procedure to windows class.
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName	= NULL;
	wndClass.lpszClassName	= szAppName;

	//Register the Window Class to the Windows System. 
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.
	hwnd = CreateWindow(szAppName,		//Window class name
		TEXT("Bezier Splines"),		//Window caption
		WS_OVERLAPPEDWINDOW,			//Window Style
		CW_USEDEFAULT,					//initial x position
		CW_USEDEFAULT,					//initial y position
		CW_USEDEFAULT,					//initial x size
		CW_USEDEFAULT,					//initial y size
		NULL,							//parent window handle
		NULL,							//window menu handle
		hInstance,						//program instance handle
		NULL);							//creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);	//This function will generate a WM_PAINT message.

	/* The message loop for this program.
	if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

void DrawBezier	(HDC hdc, POINT apt[])
{
	PolyBezier	(hdc, apt, 4);

	MoveToEx	(hdc, apt[0].x, apt[0].y, NULL);
	LineTo		(hdc, apt[1].x, apt[1].y);

	MoveToEx	(hdc, apt[2].x, apt[2].y, NULL);
	LineTo		(hdc, apt[3].x, apt[3].y);
}

//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static POINT	apt[4];
	static int		cxClient, cyClient;
	HDC				hdc;
	PAINTSTRUCT		ps;

	switch (message) //get the message
	{
	case WM_CREATE:
		return 0;
	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);

		apt[0].x = cxClient / 4;
		apt[0].y = cyClient / 2;

		apt[1].x = cxClient / 2;
		apt[1].y = cyClient / 4;

		apt[2].x =     cxClient / 2;
		apt[2].y = 3 * cyClient / 4;

		apt[3].x = 3 * cxClient / 4;
		apt[3].y =	   cyClient / 2;
		break;
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON || wParam & MK_RBUTTON)
		{
			hdc = GetDC(hwnd);

			SelectObject(hdc, GetStockObject(WHITE_PEN));
			DrawBezier(hdc, apt);

			if (wParam & MK_LBUTTON)
			{
				apt[1].x = LOWORD(lParam);
				apt[1].y = HIWORD(lParam);
			}

			if (wParam & MK_RBUTTON)
			{
				apt[2].x = LOWORD(lParam);
				apt[2].y = HIWORD(lParam);
			}

			SelectObject(hdc, GetStockObject(BLACK_PEN));
			DrawBezier(hdc, apt);
			ReleaseDC(hwnd, hdc);
		}
		return 0;
	case WM_PAINT:
		InvalidateRect(hwnd, NULL, TRUE);
		hdc = BeginPaint(hwnd, &ps);

		DrawBezier(hdc, apt);

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

运行结果




在windows98中不需要知道这些公式直接使用

PolyBezier(hdc, apt, iCount);

或者

PolyBezierTo(hdc, apt, iCount);

5.3.5 使用现有画笔

使用任何一个画线函数,windows都会使用当前选入设备环境的“画笔”来绘制直线。

画笔决定了线条的颜色,宽度和样式(实现,点或者虚线)

BLACK_PEN

WHITE_PEN

NULL_PEN


HPEN hPen;

hPen = GetStockObject(WHITE_PEN); //获得白色画笔

SelectObject(hdc, hPen); //将画笔选入设备环境

或者

SelectObject(hdc, GetStockObject(WHITE_PEN));

SelectObject返回先前一个选入设备环境的画笔句柄。

5.3.6 创建,选择和删除画笔

一般过程

CreatePen 或者CreatePenIndirect

使用以后需要调用DeleteObject函数来删除你创建的画笔

逻辑画笔是一种GDI对象,还有画刷,位图,区域,字体和调色板。

* 最终应当删除你所创建的所有GDI对象

* 当GDI被选入一个有效的设备环境时,不要删除他

* 不要删除备用对象


hPen = CreatePen(iPenStyle, iWidth, crColor);

iPenStyle 画笔样式


crColor 一个COLORREF值


LOGPEN logpen;

hPen = CreatePenIndirect(&logpen);


可以在WM_CREATE: 时候创建三种画笔

hPen1 = CreatePen(PS_SOLID, 1, 0);

hPen2 = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));

hPen3 = CreatePen(PS_POT, 0, 0);

在WM_PAINT消息选入一支画笔

SelectObject(hdc, hPen2);

...

SelectObject(hdc, hPen1);

...

在WM_DESTROY消息销毁画笔

DeleteObject(hPen1);

DeleteObject(hPen2);

DeleteObject(hPen3);

---

不要删除当前选入设备环境的句柄

SelectObject(hdc, CreatePen(PS_DASH, 0, RGB(255, 0, 0)));

...

Delete(SelectObject(hdc, GetStockObject(BLACK_PEN)));


GetObject(hPen, sizeof(LOGPEN), (LPVOID)&logpen);

获得当前选入设备环境的画笔句柄

hPen = GetCurrentObject(hdc, OBJ_PEN);

DeleteObject(SelectObject,(hdc, hPen));


如果有一个画笔句柄,LOGPEN可以通过调用GetObject函数获得

GetObject(hPen, sizeof(LOGPEN), (LPVOID) &logpen);

hPen = GetCurrentObject(hdc, OBJ_PEN);


5.3.7 填充空隙

改变windows填充空隙的背景颜色

SetBkColor(hdc, crColor);

获得当前的背景颜色 GetBkColor


将背景模式改为TRANSPARENT 来阻止Windows填充空隙

SetBkMode(hdc, TRANSPARENT);

GetBkMode         // TRANSPARENT, OPAQUE

5.3.8 绘图模式

SetROP2(hdc, iDrawMode);

iDrawMode = GetROP2(hdc);


5.4 绘制填充区域

Rectangle

Ellipse

RoundRect

Chord

Pie

Polygon

PolyPolygon


填充背景颜色用画刷

HBRUSH hbrush

hbrush = GetStockObject(GRAY_BRUSH); //获取备用画刷

SelectObject(hdc, hBrush); //将画刷选入设备环境

SelectObject(hdc, GetStockObject(NULL_PEN)); // 将NULL_PEN画笔选入设备环境

SelectObject(hdc, GetStockObject(NULL_BRUSH)); //只想绘制边框线 不想填充图形内部


5.4.1 Polygon函数和多边形填充模式

SetPolyFillMode(hdc, iMode); //设置多边形的填充模式 ALTERNATE, WINDING

一个ALTERNATE 和WINDING模式下绘制多边形填充模式的例子


#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static		TCHAR szAppName[] = TEXT("AltWind");
	HWND		hwnd;
	MSG			msg;
	WNDCLASS	wndClass;		//The window Class

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System. 
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.
	hwnd = CreateWindow(szAppName,		//Window class name
		TEXT("Alternate and Winding Fill Modes"),		//Window caption
		WS_OVERLAPPEDWINDOW,			//Window Style
		CW_USEDEFAULT,					//initial x position
		CW_USEDEFAULT,					//initial y position
		CW_USEDEFAULT,					//initial x size
		CW_USEDEFAULT,					//initial y size
		NULL,							//parent window handle
		NULL,							//window menu handle
		hInstance,						//program instance handle
		NULL);							//creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);	//This function will generate a WM_PAINT message.

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static POINT	aptFigure[10] = {10, 70, 50, 70, 50, 10, 90, 10, 90, 50,
									 30, 50, 30, 90, 70, 90, 70, 30, 10, 30};
	static int		cxClient, cyClient;
	int				i;
	HDC				hdc;
	PAINTSTRUCT		ps;
	POINT			apt[10];

	switch (message) //get the message
	{
	case WM_CREATE:
		return 0;
	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);

		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		
		SelectObject(hdc, GetStockObject(GRAY_BRUSH));

		for (i = 0; i < 10; ++i)
		{
			apt[i].x = cxClient * aptFigure[i].x / 200;
			apt[i].y = cyClient * aptFigure[i].y / 100;
		}

		SetPolyFillMode(hdc, ALTERNATE);
		Polygon(hdc, apt, 10);

		for (i = 0; i < 10; ++i)
		{
			apt[i].x += cxClient / 2;
		}

		SetPolyFillMode(hdc, WINDING);
		Polygon(hdc, apt, 10);

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

结果


5.4.2 用画刷填充内部

hBrush = CreateSolidBrush(crColor);


阴影标记画刷

hBrush = CreateHatchBrush(iHatchStyle, crColor);


hBrush = CreateBrushIndirect(&logbrush); //建立逻辑画刷

SelecObject(hdc, hBrush); 


然后

DeleteObject(hBrush);


获取画刷信息

GetObject(hBrush, sizeof(LOGBRUSH), (LPVOID)&logbrush);


5.5 GDI映射模式

 

SetMapMode(hdc, iMapMode);

iMapMode = GetMapMode(hdc);


5.5.2 设备坐标系统

GetWindowDC 获得的hdc 在GDI函数调用中,逻辑坐标会被默认映射为全窗口坐标


ClientToScreen 客户区到屏幕坐标

ScreenToClient 屏幕坐标转换到客户求


5.5.3 视口和窗口

窗口  逻辑坐标    视口  设备坐标



如果原点为(0,0) 可以简化如下


点(xWinExt, yWinExt)是在逻辑坐标系下的窗口范围,  点(xViewExt, yViewExt)是在设备坐标系下的视口范围。


Windows提供两个函数进行设备点与逻辑点的转换

DPtoLP (hdc, pPoints, iNumber);


LPtoDP(hdc, pPoints, iNumber);


5.5.4 使用MM_TEXT

SetViewportOrgEX

SetWindowOrgEx

5.5.5 度量映射模式

5.5.6 自定义映射模式


5.5.7 WHATSIZE程序

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static		TCHAR szAppName[] = TEXT("WhatSize");
	HWND		hwnd;
	MSG			msg;
	WNDCLASS	wndClass;		//The window Class

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System. 
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.
	hwnd = CreateWindow(szAppName,		//Window class name
		TEXT("What Size is the Window?"),		//Window caption
		WS_OVERLAPPEDWINDOW,			//Window Style
		CW_USEDEFAULT,					//initial x position
		CW_USEDEFAULT,					//initial y position
		CW_USEDEFAULT,					//initial x size
		CW_USEDEFAULT,					//initial y size
		NULL,							//parent window handle
		NULL,							//window menu handle
		hInstance,						//program instance handle
		NULL);							//creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);	//This function will generate a WM_PAINT message.

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

void Show(HWND hwnd, HDC hdc, int xText, int yText, int iMapMode, TCHAR *szMapMode)
{
	TCHAR szBuffer[60];
	RECT rect;

	SaveDC(hdc);
	SetMapMode(hdc, iMapMode);
	GetClientRect(hwnd, &rect);
	DPtoLP(hdc, (PPOINT)&rect, 2);

	RestoreDC(hdc, -1);

	TextOut(hdc, xText, yText, szBuffer,
			wsprintf(szBuffer, TEXT("%-20s %7d %7d %7d %7d"), szMapMode,
			rect.left, rect.right, rect.top, rect.bottom));
}

//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static TCHAR szHeading[] =
		TEXT("Mapping Mode            Left   Right     Top  Bottom");
	static TCHAR szUndLine[] =
		TEXT("------------            ----   -----     ---  ------");
	static int		cxChar, cyChar;
	HDC				hdc;
	PAINTSTRUCT		ps;
	TEXTMETRIC		tm;

	switch (message) //get the message
	{
	case WM_CREATE:
		hdc = GetDC(hwnd);
		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

		GetTextMetrics(hdc, &tm);
		cxChar = tm.tmAveCharWidth;
		cyChar = tm.tmHeight + tm.tmExternalLeading;

		ReleaseDC(hwnd, hdc);
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
		SetMapMode(hdc, MM_ANISOTROPIC);
		SetWindowExtEx(hdc, 1, 1, NULL);
		SetViewportExtEx(hdc, cxChar, cyChar, NULL);

		TextOut(hdc, 1, 1, szHeading, lstrlen(szHeading));
		TextOut(hdc, 1, 2, szUndLine, lstrlen(szUndLine));

		Show(hwnd, hdc, 1, 3, MM_TEXT, TEXT("TEXT (pixels)"));
		Show(hwnd, hdc, 1, 4, MM_LOMETRIC, TEXT("LOMETRIC (.1 mm)"));
		Show(hwnd, hdc, 1, 5, MM_HIMETRIC, TEXT("HIMETRIC (.01 mm)"));
		Show(hwnd, hdc, 1, 6, MM_LOENGLISH, TEXT("LOENGLISH (.01 in)"));
		Show(hwnd, hdc, 1, 7, MM_HIENGLISH, TEXT("HIENGLISH (.001 in)"));
		Show(hwnd, hdc, 1, 8, MM_TWIPS, TEXT("TWIPS (1/1440 in)"));
		
		EndPaint(hwnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}
运行结果如下





5.6 矩形,区域和剪裁

5.6.1 处理矩形

FillRect (hdc, &rect, bBrush); //填充矩形,不用选入画刷

FrameRect (hdc, &rect, hBrush); // 使用画刷绘制一个矩形框,但是它并不填充矩形

InvertRect (hdc, &rect); //翻转矩形内的所有像素


SetRect(&rect, xLeft, yTop, xRight, yBottom); // 直接设定rect的值


OffsetRect(&rect, x, y); //沿着x轴和y轴平移几个单位

InflateRect(&rect, x, y); //增大或减小矩形的尺寸

SetRectEmpty(&rect); //将矩形的各字段设置为0

CopyRect(&DestRect, &SrcRect); //将一个矩形结构复制到另一个矩形结构

IntersectRect(&DestRect, &SrcRect1, &SrcRect2); // 获得两个矩形的交集

UnionRect(&DestRect, &SrcRect1, &SrcRect2); //获得两个矩形的并集

bEmpty = IsRectEmpty(&rect); //判断矩形是否为空

bInRect = PtInRect(&rect, point); //判断点是否在举行内部


5.6.2 随机矩形

PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); 立即返回


GetMessage 等待同步

while(true)
{
    if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        if(msg.message = WM_QUIT)
            break;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
        //do other thing.
        
    }
}
一个随机矩形的例子
#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.
void DrawRectangle(HWND);

int cxClient, cyClient;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static		TCHAR szAppName[] = TEXT("RandRect");
	HWND		hwnd;
	MSG			msg;
	WNDCLASS	wndClass;		//The window Class

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System. 
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.
	hwnd = CreateWindow(szAppName,		//Window class name
		TEXT("Random Rectangles"),		//Window caption
		WS_OVERLAPPEDWINDOW,			//Window Style
		CW_USEDEFAULT,					//initial x position
		CW_USEDEFAULT,					//initial y position
		CW_USEDEFAULT,					//initial x size
		CW_USEDEFAULT,					//initial y size
		NULL,							//parent window handle
		NULL,							//window menu handle
		hInstance,						//program instance handle
		NULL);							//creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);	//This function will generate a WM_PAINT message.

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (TRUE)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				break;
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			DrawRectangle(hwnd);
		}
	}
	return msg.wParam;

}

//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) //get the message
	{
	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

void DrawRectangle(HWND hwnd)
{
	HBRUSH	hBrush;
	HDC		hdc;
	RECT	rect;

	if (cxClient == 0 || cyClient == 0)
		return;

	SetRect(&rect, rand() % cxClient, rand() % cyClient,
		rand() % cxClient, rand() % cyClient);

	hBrush = CreateSolidBrush(
		RGB(rand() % 256, rand() % 256, rand() % 256));
	hdc = GetDC(hwnd);

	FillRect(hdc, &rect, hBrush);
	ReleaseDC(hwnd, hdc);
	DeleteObject(hBrush);
}


5.6.3 建立和绘制区域

一个区域是对显示器一块空间的描述,可以使矩形,多边形和椭圆的组合。

hRgn = CreateRectRgn(xLeft, yTop, xRight, yBottom);

或者

hRgn = CreateRectRgnIndirect(&rect);

建立椭圆区域

hRgn = CreateEllipticRgn(xLeft, yTop, xRight, yBottom);

或者

hRgn = CreateEllipticRgnIndirect(&rect);

创建圆角矩形区域

hRgn = CreateRoundRectRgn

多边形区域

hRgn = CreatePolygonRgn(&point, iCount, iPolyFillMode);


CombineRgn  合并区域


FillRgn(hdc, hRgn, hBrush);

FrameRgn(hdc, hRgn, hBrush, xFrame, yFrame);

InvertRgn(hdc, hRgn);

PaintRgn(hdc, hRgn);

用完以后要销毁区域

DeleteObject(hRgn);


5.6.4 矩形与区域的剪裁

使用InvalidateRect 是矩形区域无效 并产生一个WM_PAINT 消息

使用GetUpdateRect 获取无效矩形坐标

使用ValidateRect函数使客户区的矩形有效

类似区域的函数

InvalidateRgn (hwnd, hRgn, bErase);

ValidateRgn(hwnd, hRgn);

将区域选入设备环境

SelectObject(hdc, hRgn);

或者

SelectClipRgn(hdc, hRgn);


当把区域选入设备环境以后可以删除它,以为GDI为剪裁区域做了一个副本。

ExcludeClipRect 从剪裁区域中去除一个矩形

InteresctClipRect 建立一个新的剪裁区域

OffsetClipRgn 把剪裁区域移动到客户区的另外一个部分


5.6.5 Clover程序

#include <windows.h>
#include <math.h>

#define TWO_PI (2.0 * 3.14159)

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.


int cxClient, cyClient;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static		TCHAR szAppName[] = TEXT("Clover");
	HWND		hwnd;
	MSG			msg;
	WNDCLASS	wndClass;		//The window Class

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System. 
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.
	hwnd = CreateWindow(szAppName,		//Window class name
		TEXT("Draw a Clover"),		//Window caption
		WS_OVERLAPPEDWINDOW,			//Window Style
		CW_USEDEFAULT,					//initial x position
		CW_USEDEFAULT,					//initial y position
		CW_USEDEFAULT,					//initial x size
		CW_USEDEFAULT,					//initial y size
		NULL,							//parent window handle
		NULL,							//window menu handle
		hInstance,						//program instance handle
		NULL);							//creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);	//This function will generate a WM_PAINT message.

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HRGN	hRgnClip;
	static int	cxClient, cyClient;
	double		fAngle, fRadius;
	HCURSOR		hCursor;
	HDC			hdc;
	HRGN		hRgnTemp[6];
	int			i;
	PAINTSTRUCT ps;

	switch (message) //get the message
	{
	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);

		hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
		ShowCursor(TRUE);

		if (hRgnClip)
			DeleteObject(hRgnClip);

		hRgnTemp[0] = CreateEllipticRgn(0, cyClient / 3,
			cxClient / 2, 2 * cyClient / 3);
		hRgnTemp[1] = CreateEllipticRgn(cxClient / 2, cyClient / 3,
			cxClient, 2 * cyClient / 3);
		hRgnTemp[2] = CreateEllipticRgn(cxClient / 3, 0,
			2 * cxClient / 3, cyClient / 2);
		hRgnTemp[3] = CreateEllipticRgn(cxClient / 3, cyClient / 2,
			2 * cxClient / 3, cyClient);
		hRgnTemp[4] = CreateRectRgn(0, 0, 1, 1);
		hRgnTemp[5] = CreateRectRgn(0, 0, 1, 1);
		hRgnClip = CreateRectRgn(0, 0, 1, 1);

		CombineRgn(hRgnTemp[4], hRgnTemp[0], hRgnTemp[1], RGN_OR);
		CombineRgn(hRgnTemp[5], hRgnTemp[2], hRgnTemp[3], RGN_OR);
		CombineRgn(hRgnClip, hRgnTemp[4], hRgnTemp[5], RGN_XOR);

		for (i = 0; i < 6; ++i)
			DeleteObject(hRgnTemp[i]);
		SetCursor(hCursor);
		ShowCursor(FALSE);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		SetViewportOrgEx(hdc, cxClient / 2, cyClient / 2, NULL);
		SelectClipRgn(hdc, hRgnClip);

		fRadius = _hypot(cxClient / 2.0, cyClient / 2.0);

		for (fAngle = 0.0; fAngle < TWO_PI; fAngle += TWO_PI / 360)
		{
			MoveToEx(hdc, 0, 0, NULL);
			LineTo(hdc, (int)(fRadius * cos(fAngle) + 0.5),
				(int)(-fRadius * sin(fAngle) + 0.5));
		}
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		DeleteObject(hRgnClip);
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

void DrawRectangle(HWND hwnd)
{
	HBRUSH	hBrush;
	HDC		hdc;
	RECT	rect;

	if (cxClient == 0 || cyClient == 0)
		return;

	SetRect(&rect, rand() % cxClient, rand() % cyClient,
		rand() % cxClient, rand() % cyClient);

	hBrush = CreateSolidBrush(
		RGB(rand() % 256, rand() % 256, rand() % 256));
	hdc = GetDC(hwnd);

	FillRect(hdc, &rect, hBrush);
	ReleaseDC(hwnd, hdc);
	DeleteObject(hBrush);
}

程序执行结果


1部分 基础知识   第1章 起步   1.1 Windows环境   1.1.1 Windows简史   1.1.2 Windows的方方面面   1.1.3 动态链接   1.2 Windows编程选项   1.2.1 API及内存管理模式   1.2.2 语言选择   1.2.3 编程环境   1.2.4 API文档   1.3 你的第一个Windows程序   1.3.1 字符模式   1.3.2 Windows对应程序   1.3.3 头文件   1.3.4 程序入口   1.3.5 MessageBox函数   1.3.6 编译、链接及运行   第2章Unicode简介   2.1 字符集简史   2.1.1 美国标准   2.1.2 美国以外的世界   2.1.3 扩展ASCII   2.1.4 双字节字符集   2.1.5 Unicode的解救方案   2.2 宽字符和c语言   2.2.1 char数据类型   2.2.2 更宽的字符   2.2.3 宽字符库函数   2.2.4 维护一个源代码文件   2.3 宽字符和Windows   2.3.1 Windows头文件的类型   2.3.2 Windows函数调用   2.3.3 Windows的字符串函数   2.3.4 在Windows中使用printf   2.3.5 格式化的消息框   2.3.6 国际化之于本书   第3章 窗口与消息   3.1 窗口的创建   3.1.1 系统结构概述   3.1.2 HELLOWIN程序   3.1.3 通盘考虑   3.1.4 窗口类的注册   3.1.5 窗口的创建   3.1.6 窗口的显示   3.1.7 消息循环   3.1.8 窗口过程   3.1.9 消息的处理   3.1.10 声音文件的播放   3.1.11 WM_PAINT消息   3.1.12 WM_DESTROY消息   3.2 Windows编程中的若干难点   3.2.1 究竟是谁调用谁   3.2.2 队列消息和非队列消息   3.2.3 速战速决   第4章 文本输出   4.1 绘制和重绘   4.1.1 WM_PAINT消息   4.1.2 有效矩形和无效矩形   4.2 GDI简介   4.2.1 设备环境   4.2.2 获取设备环境句柄:方法一   4.2.3 绘制信息结构   4.2.4 获取设备环境句柄:方法二   4.2.5 TEXTOUT函数详解   4.2.6 系统字体   4.2.7 字符大小   4.2.8 文本尺寸的度量   4.2.9 文本的格式化   4.2.10 综合使用   4.2.11 SYSMETSl.C窗口过程   4.2.12 空间不够   4.2.13 客户区的尺寸   4.3 滚动条   4.3.1 滚动条的范围和位置   4.3.2 滚动条消息   4.3.3 加入滚动条的SYSMET   4.3.4 程序的绘制代码的结构   4.4 效果更好的滚动   4.4.1 滚动条信息函数   4.4.2 最远可以卷动到哪里?   4.4.3 新的SYSMETS   4.4.4 可我不想用鼠标   第5章 绘图基础   5.1 GDI的结构   5.1.1 GDI原理   5.1.2 GDI函数调用   5.1.3 GDI的基本图形   5.1.4 其他   5.2 设备环境   5.2.1 获取设备环境句柄   5.2.2 获取设备环境的信息   5.2.3 DEVCAPSl程序   5.2.4 设备的尺寸   5.2.5 色彩ABC   5.2.6 设备环境属性   5.2.7 保存设备环境   5.3 点和线的绘制   5.3.1 设定像素   5.3.2 直线   5.3.3 边框绘制函数   5.3.4 贝塞尔样条曲线   5.3.5 使用现有画   5.3.6 创建、选择和删除画   5.3.7 填充空隙   5.3.8 绘图模式   5.4 绘制填充区域   5.4.1 Polygon函数和多边形填充模式   5.4.2 用画刷填充内部   5.5 GDI映射模式   5.5.1 设备坐标和逻辑坐标   5.5.2 设备坐标系统   5.5.3 视口和窗口   5.5.4 使用MMTEXT   5.5.5 度量映射模式   5.5.6 自定义的映射模式   5.5.7 WHATSIZE程序   5.6 矩形、区域和剪裁   5.6.1 处理矩形   5.6.2 随机矩形   5.6.3 建立和绘制区域   5.6.4 矩形与区域的剪裁   5.6.5 CLOVER程序   第6章 键盘   6.1 键盘基础   6.1.1 忽略键盘   6.1.2 谁获得了焦点?   6.1.3 队列和同步   6.1.4 击键和字符   6.2 击键消息   6.2.1 系统键击和非系统键击   6.2.2 虚拟键代码   6.2.3 1param信息   6.2.4 转义状态   6.2.5 使用击键消息   6.2.6 为SYSMETS加上键盘处理功能   6.3 字符消息   6.3.1 四类字符消息   6.3.2 消息排序   6.3.3 控制字符的处理   6.3.4 死字符消息   6.4 键盘消息和字符集   6.4.1 KEYVIEW1程序   6.4.2 非英语键盘问题   6.4.3 字符集和字体   6.4.4 Unicode解决方案   6.4.5 TrueType字体和大字体   6.5 插入符号(不是光标)   6.5.1 一些关于插入符号的函数   6.5.2 TYPER程序   第7章 鼠标   7.1 鼠标的基础知识   7.1.1 一些基本术语   7.1.2 鼠标的复数形式是什么?   7.2 客户区鼠标消息   7.2.1 简单的鼠标处理示例   7.2.2 处理Shift键   7.2.3 鼠标双击   7.3 非客户区鼠标消息   7.3.1 击中测试消息   7.3.2 消息引发消息   7.4 程序中的击中测试   7.4.1 一个假想的例子   7.4.2 一个简单的程序   7.4.3 使用键盘模仿鼠标操作   7.4.4 在CHECKER中增加键盘接口   7.4.5 在击中测试中使用子窗口   7.4.6 CHECKER程序中的子窗口   7.4.7 子窗口和键盘   7.5 捕获鼠标   7.5.1 设计一个矩形   7.5.2 捕获的解决方案   7.5.3 BLOKOUT2程序   7.6 鼠标的滚轮   第8章 计时器   8.1 计时器的基本知识   8.1.1 系统和计时器   8.1.2 计时器消息不是异步的   8.2 使用计时器的三种方法   8.2.1 方法一   8.2.2 方法二   8.2.3 方法三   8.3 使用计时器作为时钟   8.3.1 数字时钟   8.3.2 获取当前时间   8.3.3 显示数字和冒号   8.3.4 考虑国际化   8.3.5 模拟时钟   8.4 在状态报告上使用计时器   第9章 子窗口控件   9.1 按钮类   9.1.1 创建子窗口   9.1.2 子窗口传递信息给父窗口   9.1.3 父窗口传递信息给子窗口   9.1.4 按钮   9.1.5 复选框   9.1.6 单选按钮   9.1.7 组合框   9.1.8 改变按钮文本   9.1.9 可见的按钮和启用的按钮   9.1.10 按钮和输入焦点   9.2 控件和颜色   9.2.1 系统颜色   9.2.2 按钮的颜色   9.2.3 WMCTLCOLORBTN消息   9.2.4 自绘按钮   9.3 静态类   9.4 滚动条类   9.4.1 COLORS1程序   9.4.2 自动键盘接口   9.4.3 窗口子类   9.4.4 背景着色   9.4.5 给滚动条和静态文本着色   9.5 编辑类   9.5.1 编辑类的样式   9.5.2 编辑控件的通知消息   9.5.3 使用编辑控件   9.5.4 传递给编辑控件的消息   9.6 列表框类   9.6.1 列表框的样式   9.6.2 向列表框中添加字符串   9.6.3 项目的选择和提取   9.6.4 接收来自列表框的消息   9.6.5 简单的列表框程序   9.6.6 列出文件   9.6.7 Windows的HEAD程序   第10章 菜单和其他资源   10.1 图标、鼠标指针、字符串和自定义资源   10.1.1 向程序添加图标   10.1.2 获得图标的句柄   10.1.3 在应用程序中使用图标   10.1.4 使用自定义鼠标指针   10.1.5 字符串资源   10.1.6 自定义资源   10.2 菜单   10.2.1 和菜单有关的概念   10.2.2 菜单结构   10.2.3 定义菜单   10.2.4 在程序中引用菜单   10.2.5 菜单和消息   10.2.6 范例程序   10.2.7 菜单设计中的规范   10.2.8 定义菜单的繁琐方式   10.2.9 浮动弹出菜单   10.2.1 0使用系统菜单   10.2.1 1改变菜单   10.2.1 2其他菜单命令   10.2.1 3菜单的另类用法   10.3 键盘加速键   10.3.1 为什么你应该使用键盘加速键   10.3.2 指定加速键的一些规则   10.3.3 加速键表   10.3.4 加载加速键表   10.3.5 翻译按键   10.3.6 接收加速键消息   10.3.7 带有菜单和加速键的POPPAD程序   10.3.8 启用菜单项   10.3.9 处理菜单项   第11章 对话框   11.1 模态对话框   11.1.1 创建一个About对话框   11.1.2 对话框及其模板   11.1.3 对话框过程   11.1.4 激活对话框   11.1.5 主题变换   11.1.6 更复杂的对话框   11.1.7 对话框控件的应用   11.1.8 OK和Cancel按钮   11.1.9 避免全局变量   11.1.1 0Tab停靠和选项组   11.1.1 1在对话框上绘图   11.1.1 2关于对话框的其他函数.   11.1.1 3定义程序自己的控件   11.2 非模态对话框   11.2.1 模态与非模态对话框的区别   11.2.2 新的COLORS程序   11.2.3 HEXCALC:窗口还是对话框?   11.3 公用对话框   11.3.1 完善POPPAD   11.3.2 Unicode文件的读/写操作   11.3.3 改变字体   11.3.4 查找和替换   11.3.5 只调用一个函数的Windows程序   ……   第Ⅱ部分 关于图的那些事儿   第Ⅲ部分 高级主题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值