windows程序设计(十一)图形基础(3)

画笔的建立、选择和删除

使用函式 CreatePen 或 CreatePenIndirect 建立一个逻辑画笔,这仅仅是对画笔的描述。这些函式传回逻辑画笔的代号;然后,呼叫 SelectObject 将画笔选进装置内容。现在,就可以使用新的画笔来画线了。 在任何时候,都只能有一种画笔选进装置内容。在释放装置内容(或者在选择 了另一种画笔到装置内容中)之后,就可以呼叫 DeleteObject 来删除所建立的逻辑画笔了。

在删除後,该画笔的代号就不再有效了。 逻辑画笔是一种GDI物件,它可以建立的六种 GDI 物件之一,其他五种是画刷、点阵图、区域、字体和调色盘。除了调色盘之外,这些物件都是通过 SelectObject 选进装置内容的。 在使用画笔等 GDI 物件时,应该遵守以下三条规则:
最后要删除自己建立的所有 GDI 物件。
当 GDI 物件正在一个有效的装置内容中使用时,不要删除它。
不要删除现有物件。
CreatePen的用法如下所示:

hPen = CreatePen (iPenStyle, iWidth, crColor) ;

iPenStyle参数确定画笔是实线、点线还是虚线,其参数如下图所示:
在这里插入图片描述
iWidth 值为 0 则意味著画笔宽度为一个图素。现有画笔是一个图素宽。 如果指定的是点划线或者虚线式画笔样式,同时又指定一个大于 1 的实际宽度, 那么 Windows 将使用实线画笔来代替。 crColor是一个COLORREF 类型的值,可以指定画笔的颜色。
除此之外,还可以指定一个 LOGPEN结构的画刷,使用CreatePenIndirect 函数来建立该画笔。在调用该函数之前,首先需要定义一个LOGPEN的形态结构:

LOGPEN logpen;
hPen = CreatePenIndirect (&logpen) ;

对于LOGPEN结构,如下所示:

typedef struct tagLOGPEN {
  UINT     lopnStyle;  //画笔样式
  POINT    lopnWidth;  //按逻辑单位的画笔宽度
  COLORREF lopnColor;  //画笔颜色
} LOGPEN, *PLOGPEN, *NPLOGPEN, *LPLOGPEN;

注意,CreatePen和CreatePenIndirect函数不需要装置内容代号作为参数。 这些函式建立与装置内容没有联系的逻辑画笔。直到呼叫 SelectObject 之后, 画笔才与装置内容发生联系。
下面一个建立、选择、删除画笔的例子:

static HPEN hPen1, hPen2, hPen3 ; 

//在WM_CREATE期间
hPen1 = CreatePen (PS_SOLID, 1, 0) ;
hPen2 = CreatePen (PS_SOLID, 3, RGB (255, 0, 0)) ;
hPen3 = CreatePen (PS_DOT, 0, 0) ;

//在WM_PAINT期间
SelectObject (hdc, hPen2) ;//选择画笔

//在WM_DESTROY 期间
DeleteObject (hPen1) ; 
DeleteObject (hPen2) ;
DeleteObject (hPen3) ;

我们可能想要在每个 WM_PAINT 讯息处理期间建立画笔,并在呼叫 EndPaint 之后删除它们,也可以在呼叫 EndPaint 之前删除它们, 但是要小心,不要删除装置内容中目前选择的画笔。
下面是另一种创建删除画笔的方法:

SelectObject (hdc, CreatePen (PS_DASH, 0, RGB (255, 0, 0))) ; 
DeleteObject (SelectObject (hdc, GetStockObject (BLACK_PEN))) 

SelectObject 将传回装置内容中上一次选择的画笔代号。在没有创建画笔代号的情况下,可以通过呼叫 SelectObject 将 BLACK_PEN 选进装置内容,并删除从 SelectObject 传回的值。
如果有一个画笔的代号,就可以通过调用 GetObject 取得 LOGPEN 结构各个 成员的值:

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

填入空隙

当时使用虚线形式的画笔画线时,虚线之间的空隙会插入相应的颜色。这些空隙取决于两个装置内容:背景模式和背景颜色。默认的背景模式为OPAQUE,在这种方式下,Windows 使用背景色来填入空隙,内定的背景色为白色。
可以使用以下的函数来更改填入空白的背景颜色:

SetBkColor (hdc, crColor) ;

可以通过用 GetBkColor 来取得装置内容中定义的目前背景色。
通过将背景模式转换为 TRANSPARENT,可以阻止 Windows 填入空隙:

SetBkMode (hdc, TRANSPARENT) ;

在这之后,Windows 将忽略背景色,并且不填入空隙,可以通过调用GetBkMode 来取得目前背景模式。

绘制填入区域

下表为七个用来画带边缘的填入图形函数:

函数图形
Rectangle直角矩形
Ellipse椭圆
RoundRect圆角矩形
Chord椭圆周上的弧,两端以弦连接
Pie椭圆上的圆形图
Polygon多边形
PolyPolygon多个多边形

图形以目前装置内容中选择的画刷来填入。内定情况下,默认的画刷为白色。Windows 定义六种现有画刷:WHITE_BRUSH、 LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、BLACK_BRUSH 和 NULL_BRUSH (也 叫 HOLLOW_BRUSH)。可以将任何一种现有画刷选入您的装置内容中,就和选择一种画笔一样。windows将 HBRUSH 定义为画刷的数据类型。
可以首先定义一个画刷:

HBRUSH hBrush ; 
hBrush = GetStockObject (GRAY_BRUSH) ; //使用GetStockObject 函数取得GRAY_BRUSH画刷
SelectObject (hdc, hBrush) ; //选入装置内容

如果想画一个没有边界框的图形,可以将NULL_PEN选进装置内容:

SelectObject (hdc, hBrush) ;

除此之外,还可以自订画刷。

多边形填入方式

首先,介绍一些已经用过的Polygon 函数;

BOOL Polygon(
  HDC         hdc,
  const POINT *apt,
  int         cpt
);

hdc为装置就句柄,apt参数是一个POINT类型的数组,cpt是点的个数。如果该数组中的最后一个点与第一个点不同,则 Windows 将会再加一条线,将最后一个点与第一个点连起来。
再介绍一下PolyPolygon 函数:

BOOL PolyPolygon(
  HDC         hdc,
  const POINT *apt,
  const INT   *asz,
  int         csz
);

其中,前三个参数与Polygon函数的功能类似,最后一个参数csz为所画多边形的个数。
对于填入内部的方式,可以使用SetPolyFillMode 函数来决定:

SetPolyFillMode (hdc, iMode) ;

其中,iMode参数为模式,有ALTERNATE和WINDING 两种填入方式。
对于 ALTERNATE 方式,可以设想从一个无穷大的封闭区域内部的点画线,只有假想的线穿过了奇数条边界线时,才填入封闭区域。
在 WINDING 方式下要确定一个封闭区域是否被填入,仍旧可以设想从那个无穷大的区域画线。如果假想的线穿过了奇数条边界线,区域就被填入,这 和 ALTERNATE 方式一样。如果假想的线穿过了偶数条边界线,则区域可能被填入也可能不被填入。如果一个方向(相对于假想线)的边界线数与另一个方向的边界线数不相等,就填入区域。
接下的一个例子,就是有关这两种方式:

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;
	HDC hdc;
	PAINTSTRUCT ps;
	int i;
	RECT rect;
	POINT  apt[10];

	switch (message) {
	case WM_SIZE:
		cxClient = LOWORD(lparam);
		cyClient = HIWORD(lparam);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		GetClientRect(hwnd, &rect);
		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 / 200;
		}
		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:  //正在销毁窗口
					  //向消息序列投递一个WM_QUIT消息,促使GetMessage函数返回0,结束消息循环
		::PostQuitMessage(0);
		return 0;
	}
	return ::DefWindowProc(hwnd, message, wparam, lparam);
}

程序运行的结果如下所示:
在这里插入图片描述

对于ALTERNATE和WINDING 的理解

ALTERNATE模式填充方法:对于一个多边形来讲,从左到右画一条线无限长的线,当穿过了奇数边才填充,经过偶数边结束填充。
WINDING模式的填充方法:与 ALTERNATE类似,但是当这条线经过偶数边时需要经过判断,如果绘制封闭区域线段的方向为奇数个方向时,该封闭区域被填充,偶数个方向则不填充。
对于刚才的例子,我们可以很清楚的明白两种模式的区别:
在这里插入图片描述
这是使用ALTERNATE模式进行填充时的情况,我们可以在其中作几条很长的线:
在这里插入图片描述
从上面的图可以看到,对每一种形态的封闭形状画不同颜色的线,对穿过的每一条线进行了标号,可以看到,对于每一种封闭图形的情况只有从奇数号的线开始填充,到偶数号的线结束。
这是使用WINDING模式进行填充时的情况:
在这里插入图片描述
在WINDING模式下,需要对通过偶数边的封闭区域进行判断,首先我们先把这个图形的画线顺序搞清楚,对于上面的例子,其画线的顺序如下图所示:
在这里插入图片描述
可以看到对于区域5,它画出的顺序只有一个顺序,顺序数为奇数个。但是对于区域4,它画线的顺序有两种,顺序数位偶数个。所以在填充的时候之填充5区域,而不填充四区域。
在这里插入图片描述

参考资料:
[1]《Windows程序设计(第五版)》
[2]https://docs.microsoft.com/zh-cn/welcome-to-docs
[3]https://blog.csdn.net/goki123/article/details/5216303
[4]https://blog.csdn.net/qq_33001647/article/details/51830004?depth_1-utm_source=distribute.pc_relevant.nonetask&utm_source=distribute.pc_relevant.none-task

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值