第五章 图像基础(绘制填入区域)

来源:互联网 发布:热血三国马装数据 编辑:程序博客网 时间:2024/05/29 08:37

绘制填入区域

现在再更进一步,从画线到画图形。Windows中七个用来画带边缘的填入图形的函数列于表5-3中。

表5-3

 

函数

图形

Rectangle

直角矩形

Ellipse

椭圆

RoundRect

圆角矩形

Chord

椭圆周上的弧,两端以弦连接

Pie

椭圆上的饼图

Polygon

多边形

PolyPolygon

多个多边形

Windows用设备内容中选择的目前画笔来画图形的边界框,边界框还使用目前背景方式、背景色彩和绘图方式,这跟Windows画线时一样。关于直线的一切也适用于这些图形的边界框。

图形以目前设备内容中选择的画刷来填入。内定情况下,使用现有对象,这意味着图形内部将画为白色。Windows定义六种现有画刷:WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、BLACK_BRUSH和NULL_BRUSH (也叫HOLLOW_BRUSH)。您可以将任何一种现有画刷选入您的设备内容中,就和您选择一种画笔一样。Windbws将HBRUSH定义为画刷的句柄,所以可以先定义一个画刷句柄变量:

HBRUSH hBrush ;

您可以通过呼叫GetStockObject来取得GRAY_BRUSH的句柄:

hBrush = GetStockObject (GRAY_BRUSH) ;

您可以呼叫SelectObject将它选进设备内容:

SelectObject (hdc, hBrush) ;

现在,如果您要画上表中的任一个图形,则其内部将为灰色。

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

SelectObject (hdc, GetStockObject (NULL_PEN)) ;

如果您想画出图形的边界框,但不填入内部,则将NULL_BRUSH选进设备内容:

SelectObject (hdc, GetStockobject (NULL_BRUSH) ;

您也可以自订画刷,就如同您自订画笔一样。我们将马上谈到这个问题。

Polygon函数和多边形填入方式

我已经讨论过了前五个区域填入函数,Polygon是第六个画带边界框的填入图形的函数,该函数的呼叫与Polyline函数相似:

Polygon (hdc, apt, iCount) ;

其中,apt参数是POINT结构的一个数组,iCount是点的数目。如果该数组中的最后一个点与第一个点不同,则Windows将会再加一条线,将最后一个点与第一个点连起来(在Polyline函数中,Windows不会这么做)。PolyPolygon函数如下所示:

PolyPolygon (hdc, apt, aiCounts, iPolyCount) ;

该函数绘制多个多边形。最后一个参数给出了所画的多边形的个数。对于每个多边形,aiCounts数组给出了多边形的端点数。apt数组具有全部多边形的所有点。除传回值以外,PolyPolygon在功能上与下面的代码相同:

for (i = 0, iAccum = 0 ; i < iPolyCount ; i++)

{

Polygon (hdc, apt + iAccum, aiCounts[i]) ;

iAccum += aiCounts[i] ;

}

对于Polygon和PolyPolygon函数,Windows使用定义在设备内容中的目前画刷来填入这个带边界的区域。至于填入内部的方式,则取决于多边形填入方式,您可以用SetPolyFillMode函数来设定:

SetPolyFillMode (hdc, iMode) ;

内定情况下,多边形填入方式是ALTERNATE,但是您可以将它设定为WINDING。两种方式的区别参见图5-15所示。

 

 

图5-15 用两种多边形填入方式画出的图:ALTERNATE(左)和WINDING(右)

首先,ALTERNATE和WINDING方式之间的区别很容易察觉。对于ALTERNATE方式,您可以设想从一个无穷大的封闭区域内部的点画线,只有假想的线穿过了奇数条边界线时,才填入封闭区域。这就是填入了星的角而中心没被填入的原因。

五角星的例子使得WINDING方式看起来比实际上更简单一些。在绘制单个的多边形时,大多数情况下,WINDING方式会填入所有封闭的区域。但是也有例外。

在WINDING方式下要确定一个封闭区域是否被填入,您仍旧可以设想从那个无穷大的区域画线。如果假想的线穿过了奇数条边界线,区域就被填入,这和ALTERNATE方式一样。如果假想的线穿过了偶数条边界线,则区域可能被填入也可能不被填入。如果一个方向(相对于假想线)的边界线数与另一个方向的边界线数不相等,就填入区域。

例如,考虑图5-16中的物体。在线的箭头指出了画线的方向。两种方式都会填入三个封闭的L形区域,号码从1到3。号码为4和5的两个小内部区域,在ALTERNATE方式下不会被填入。但是,在WINDING方式下,号码为5的区域会被填入,因为从区域内必须穿过两条相同方向的线才能到达图形外部。号码为4的区域不会被填入,因为必须穿过两条方向相反的线。

如果您怀疑Windows没有这么聪明,那么程序5-5 ALTWIND会展示给您看。

 

 

图5-16 WINDING方式不能填入所有内部区域的图形

程序5-5  ALTWIND

ALTWIND.C

/*-------------------------------------------------------------------

ALTWIND.C -- Alternate and Winding Fill Modes

(c) Charles Petzold, 1998

-------------------------------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

static TCHAR szAppName[] = TEXT ("AltWind") ;

HWND hwnd ;

MSG msg ;

WNDCLASS wndclass ;

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.lpfnWndProc= WndProc ;

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 ;



if (!RegisterClass (&wndclass))

{

MessageBox ( NULL, TEXT ("Program requires Windows NT!"),

szAppName, MB_ICONERROR) ;

return 0 ;

}



hwnd = CreateWindow (szAppName, TEXT ("Alternate and Winding Fill Modes"),

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;



ShowWindow (hwnd, iCmdShow) ;

UpdateWindow (hwnd) ;



while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

}


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 ;

int i ;

PAINTSTRUCT ps ;

POINT apt[10] ;



switch (message)

{

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) ;

}

图形的坐标(划分为100×100个单位)储存在aptFigure数组中。这些坐标是依据显示区域的宽度和高度划分的。程序显示图形两次,一次使用ALTERNATE填入方式,另一次使用WINDING方式。结果见图5-17。

 

 

图5-17 ALTWIND的显示

用画刷填入内部

Rectangle、RoundRect、Ellipse、Chord、Pie、Polygon和PolyPolygon图形的内部是用选进设备内容的目前画刷(也称为「图样」)来填入的。画刷是一个8×8的位图,它水平和垂直地重复使用来填入内部区域。

当Windows用混色的方法来显示多于可从显示器上得到的色彩时,实际上是将画刷用于色彩。在单色系统上,Windows能够使用黑色和白色图素的混色建立64种不同的灰色,更精确地说,Windows能够建立64种不同的单色画刷。对于纯黑色,8×8位图中的所有位均为0。第一种灰色有一位为1,第二种灰色有两位为1,以此类推,直到8×8位图中所有位均为1,这就是白色。在16色或256色显示系统上,混色也是位图,并且可以得到更多的色彩。

Windows还有五个函数,可以让您建立逻辑画刷,然后就可使用SelectObject将画刷选进设备内容。与逻辑画笔一样,逻辑画刷也是GDI对象。您建立的所有画刷都必须被删除,但是当它还在设备内容中时不能将其删除。

下面是建立逻辑画刷的第一个函数:

hBrush = CreateSolidBrush (crColor) ;

函数中的Solid并不是指画刷为纯色。在将画刷选入设备内容中时,Windows建立一个混色色的位图,并为画刷使用该位图。

您还可以使用由水平、垂直或者倾斜的线组成的「影线标记(hatch marks)」来建立画刷,这种风格的画刷对着色条形图的内部和在绘图机上进行绘图最有用。建立影线画刷的函数为:

hBrush = CreateHatchBrush (iHatchStyle, crColor) ;

iHatchStyle参数描述影线标记的外观。图5-18显示了六种可用的影线标记风格。

 

 

图5-18 六种影线画刷风格

CreateHatchBrush中的crColor参数是影线的色彩。在将画刷选进设备内容时,Windows将这种色彩转换为与之最相近的纯色。影线之间的区域根据设备内容中定义的背景方式和背景色来着色。如果背景方式为OPAQUE,则用背景色(它也被转换为纯色)来填入线之间的空间。在这种情况下,影线和填入色都不能是混色而成的颜色。如果背景方式为TRANSPARENT,则Windows只画出影线,不填入它们之间的区域。

您也可以使用CreatePatternBrush和CreateDIBPatternBrushPt建立自己的位图画刷。

建立逻辑画刷的第五个函数包含其它四个函数:

hBrush = CreateBrushIndirect (&logbrush) ;

变量logbrush是一个型态为LOGBRUSH(「逻辑画刷」)的结构,该结构的三个字段如表5-4所示,lbStyle字段的值确定了Windows如何解释其它两个字段的值:

表5-4

 

lbStyle (UINT)

lbColor (COLORREF)

lbHatch (LONG)

BS_SOLID

画刷的色彩

忽略

BS_HOLLOW

忽略

忽略

BS_HATCHED

影线的色彩

影线画刷风格

BS_PATTERN

忽略

位图的句柄

BS_DIBPATTERNPT

忽略

指向DIB的指标

前面我们用SelectObject将逻辑画笔选进设备内容,用DeleteObject删除画笔,用GetObject来取得逻辑画笔的信息。对于画刷,同样能使用这三个函数。一旦您取得到了画刷句柄,就可以使用SelectObject将该画刷选进设备内容:

SelectObject (hdc, hBrush) ;

然后,您可以使用DeleteObject函数删除所建立的画刷:

DeleteObject (hBrush) ;

但是,不要删除目前选进设备内容的画刷。

如果您需要取得画刷的信息,可以呼叫GetObject:

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

其中,logbrush是一个型态为LOGBRUSH的结构。